Python은 컴퓨터 프로그래밍 언어다.
컴퓨터 프로그래밍 언어는 컴퓨터에게 여러 가지 일을 시킬 때 사용하는 언어다.
간결하고 알기 쉬운 문법과 강력한 기능을 가진다.
초보자 교육에서부터 웹 개발, 과학계산, 데이터 분석, 인공지능 등 다양한 분야에 활용할 수 있다.
1991년 네덜란드 프로그래머 휘도 판 로숨(Guido van Rossum)이 첫 버전을 발표했다.
현재 3.6이 최신 버전이다.
arXiv는 물리, 수학, 컴퓨터 분야에서 정식 출판 전인 논문을 올리는 사이트이다.
2017년 10월 arXiv에서 언급된 딥러닝 프레임워크들을 보면 tensorflow, keras, theano, pythorch 등 다수가 Python 프레임워크다.
(출처: https://cran.r-project.org/web/packages/keras/vignettes/why_use_keras.html)
천문학 논문에서 언급된 소프트웨어/프로그래밍 언어
(출처: https://www.datacamp.com/community/blog/python-scientific-computing-case)
Python은 공식 홈페이지에서도 무료로 다운 받을 수 있지만, 데이터 분석에는 많은 추가 패키지가 필요하다. 패키지들 중에는 윈도에서 설치가 까다로운 것들도 있다. 이런 문제를 피하기 위해 아나콘다를 설치한다. 아나콘다는 Python과 여러 패키지들을 쉽게 설치/관리할 수 있는 배포판이다.
아나콘다를 설치하려면 구글에서 "anaconda download"를 검색하거나 https://www.anaconda.com/download 에 직접 접속한다.
운영체제(Windows, macOS, Linux)를 선택 후 Python 버전을 선택한다. Python 버전은 최신 버전인 3.6을 선택한다. 설치 파일을 받고, 실행을 시키면 설치가 시작된다.
Next 를 클릭하고,
I Agree 를 클릭한다.
이후로는 윈도를 기준으로 설명한다. 맥이나 리눅스에서는 옵션 선택 없이 Next만 누르면 된다.
Just me를 선택하고 Next 를 클릭한다.
다음은 아나콘다 저장 경로를 선택한다. 경로를 바꾸지말고 Next 를 클릭한다.
고급 설치 옵션을 선택한다. 아나콘다의 설치 경로를 PATH 환경 변수에 추가를 해주고 기본 Python으로 설정하는 옵션이다. 둘 다 체크를 해주고 Install 을 클릭한다.
설치가 완료되면 VS Code 설치 여부를 묻는데, 이 때 Skip 을 클릭하면 된다.
작업을 희망하는 폴더의 경로창을 클릭해, jupyter notebook 을 친다.
새로운 코맨드 창이 뜬다. 이 코맨드 창은 작업 기간 동안 끄지 않고 유지해야 주피터 노트북을 사용할 수 있다.
새로운 인터넷창 혹은 탭이 동시에 뜬다.
만약 새 창이 뜨지 않으면, 수동으로 코맨드창의 주소를 복사해서 붙여놓으면 된다.
New 의 Python 3 를 클릭하면 새로운 파일을 만들 수 있다.
파일명을 클릭하면 이름을 수정할 수 있다.
셀의 테두리 색이 초록색이면 편집(edit) 모드라는 의미이다.
편집 모드일 때 셀에 내용을 칠 수 있다.
단축키:
Enter셀의 테두리 색이 파란색이면 명령(command) 모드라는 의미이다.
명령 모드는 셀에 어떠한 명령을 내리고 싶을 때 사용한다. 일반적으로 code 모드에서 markdown 모드로 변환할때 많이 사용한다.
단축키:
Esc셀을 추가하고 싶을 때는 셀 추가 아이콘(+) 을 클릭한다.
단축키:
명령모드 + a명령모드 + b셀을 지우고 싶을때는 셀 제거 아이콘(가위)를 클릭한다.
단축키:
명령모드 + x명령모드 + z셀을 복사하고 싶을때는 복사하기 버튼을 누른다.
단축키: 명령모드 + c
셀을 붙여넣고 싶을때는 붙여넣기 버튼을 누른다.
단축키:
명령모드 + v명령모드 + Shift + v셀을 위아래로 이동할 수 있다. 명령모드 에서 화살표 버튼 을 클릭하면 셀을 이동시킬 수 있다.
명령모드에서 shift를 같이 누르고 Edit - Merge Cell 을 누르면 두 셀을 합칠 수 있다.
단축키:
Shift + m편집모드에서 코드를 입력할 수 있다. 코드를 실행시키고 싶으면, 실행하기 버튼을 누르면 실행된다.
단축키:
shift + EnterCell 의 Run All 을 클릭하면 전체 셀이 실행된다.
강제로 커널을 멈추고 싶다면, 멈추기 버튼 을 클릭한다.
단축키:
ctrl + c명령모드 + i + iHome 에서 파일을 선택해 Shutdown 할 수도 있다.
실행 결과가 길어질 경우, 결과의 왼쪽 회색 구역을 클릭함에 따라 여러 방법으로 결과를 볼 수 있다.
한번 클릭 은 펼쳐보기를 의미한다.
더블 클릭은 숨기기를 의미한다.
주피터 노트북에 마크다운 모드에서 제목을 추가할 수 있다. 제목의 크기는 샵(#)의 개수로 정해진다.
단축키:
명령모드 + 2샵(#)으로 코드에 주석을 추가할 수 있다.
한 셀을 마크다운 셀로 편집할 수 있다.
단축키:
명령모드 + m커널을 재시작하고 싶을 경우, 커널 재시작 버튼 을 클릭해 Restart 버튼을 누른다.
단축키:
명령모드 + 0 + 0주피터 파일은 자동 저장이 된다. 하지만, 바로 저장하고 싶을때는 저장 버튼을 누른다.
단축키:
ctrl + sFile - Make a Copy 로 파일의 복사본을 만들 수 있다.
복사본이 만들어졌다.
File - Download as - HTML 을 클릭하면, 주피터 노트북을 HTML 파일로 내보낼 수 있다.
HTML 파일은 다음과 같이 만들어진다.
.py 파일로 내보내기File - Download as - Python 을 클릭하면, 주피터 노트북을 파이썬 파일로 내보낼 수 있다.
.py 파일은 파이썬 에디터로 수정하고 실행할 수 있다.
파이썬에서 수를 다루는 자료형에는 정수(int)와 실수(float) 두 가지가 있다.
파이썬에서 계산은 일반 계산방법과 비슷하다.
2 + 4
6
2 - 4
-2
2 * 4
8
2 / 4
0.5
2 ** 4 # 2의 4제곱
16
5 % 3 # 5÷3 의 나머지
2
5 / 2 # 5÷2 의 값
2.5
5 // 2 # 5÷2 의 몫
2
변수(variable)란 프로그래밍에서 값에 이름을 붙인 것이다.
파이썬에서 변수는 등호(=)로 할당한다.
a = 1 # 변수 지정
a # 확인
1
초보자 단계에서 종종 변수와 문자열을 혼동할 수 있다.
철수 = 10 이라고 변수를 지정한 후 철수 와 '철수' 의 차이를 알아보자.
철수 = 10
print(철수) # 철수
print("철수") # "철수"
10
철수
첫번째 출력문은 10의 값을 가지는 변수 철수 의 값을 출력한다.
두번째 출력문은 문자열 "철수" 를 출력한다.
따옴표 " " 에 감싸쥔 내용은 문자열로 인식된다는 점을 잊지말자.
파이썬의 변수는 알파벳 대소문자, 숫자, 언더바(_) 그리고 한글, 한자 등을 사용할 수 있다. 단, 첫 글자는 숫자로 시작할 수 없다. 공백이나 특수문자, 문장 분호는 변수의 이름으로 쓸 수 없다.
일반적으로, 변수 이름은 영어로 짓는다. 이때 대소문자는 구별한다. 즉 A와 a는 다른 변수이다.
var = 1
var
1
원한다면, 한글로도 지정이 가능하다.
변수 = 1
변수
1
언더바 (_)는 변수 이름이 길어질 때 주로 공백을 대신 해서 사용한다.
my_number = 1
my_number
1
변수 이름 첫 글자에 숫자를 쓸 수 없다.
1var = 1
File "<ipython-input-2-460332d99e8f>", line 1
1var = 1
^
SyntaxError: invalid syntax
이름에 하이픈(-) 을 사용할 수 없다.
var-1 = 1
File "<ipython-input-8-fad43703b7f0>", line 1
var-1 = 1
^
SyntaxError: can't assign to operator
중복되는 변수가 있다면, 나중에 정의된 변수가 선택된다.
a = 1 # a에 1을 넣고
a = 100 # 같은 이름을 가진 변수에 100을 넣은 후,
a # a 확인
100
나중에 지정된 100 이 저장됐다.
파이썬에서 다음 코드를 자주 보게된다.
a = 0 # a는 0
a = a + 1 # a에 1을 더한 후, a라고 저장한다
a # a를 확인해보면
1
결과는 0이 아닌 1이다.
a = a + 1 # 한번 더, a를 a 더하기 1이라고 저장한다.
a # a를 확인해보면
2
2 라는 값을 갖는다.
한 번에 여러 변수를 지정할수 있다. 아래와 같이 입력할 경우 두 변수 a와 b에는 모두 같은 값 1이 할당된다.
a = b = 1
a
1
b
1
아래와 같이 입력할 경우에는 두 변수 a, b에 각각 1과 2가 할당된다.
a, b = 1, 2
a
1
b
2
아래와 같은 방식으로 두 변수의 값을 서로 맞바꿀 수도 있다.
a, b = b, a
a
2
b
1
만약, 변수명을 정의하지 않고 실행시킨다면, NameError가 발생한다.
aa # 정의하지 않은 변수
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-19-9cd599a35238> in <module>()
----> 1 aa
NameError: name 'aa' is not defined
이때는, 변수명을 다시 확인하거나, 주피터 노트북에서 실행시켰는지 다시 확인한다.
변수를 삭제하고 싶을때는, del 내장 함수를 사용한다.
a = 1 # a는 1이라고 지정한 후,
del a # del 함수를 사용해 지워본다
a # a를 확인해보면,
NameError Traceback (most recent call last)
<ipython-input-19-9cd599a35238> in <module>()
----> 1 a
NameError: name 'a' is not defined
파이썬이 a 를 찾지 못했다고 NameError 를 띄운다.
철수는 100만원을 예금하고 연이율 5%의 복리로 10년간 예치하였다고 한다. 철수의 소지 금액은 매년 어떻게 변할지 구하시오.
1년 = 105
2년 = 110.25
...
문자열은 문자로 이뤄진 자료 집합이다.
'This is a string'
문자열은 따옴표를 사용해 만들거나,
a = '문자열입니다'
b = "문자열입니다"
str 내장 함수로, 자료형을 바꿀 수 있다.
str(123) # 정수를 문자열로
'123'
문자열 안에 따옴표를 사용할때는, 문자열이 중간에 끊겨 SyntaxError가 발생하지 않게 주의를 기울여야 한다.
"영희가 물었다. "철수야, 숙제했어?"" # 큰 따옴표 안에 큰 따옴표 사용
File "<ipython-input-31-b35db4e69346>", line 1
"영희가 물었다. "철수야, 숙제했어?""
^
SyntaxError: invalid syntax
만약, 문자 열 안에 큰 따옴표를 사용하고 싶다면, 밖에 따옴표를 작은 따옴표로 감싸주면 된다.
'영희가 물었다. "철수야, 숙제했어?"' # ' ' 로 " " 감싸기
따옴표 세개(""")를 연속으로 사용하면, 다시 따옴표 세 개가 연속으로 나올 때까지
줄바꿈을 포함해서 모든 글자를 문자열로 인식한다.
"""
영희가 물었다. "철수야, 숙제했어?"
철수는 생각했다. '영희는 숙제를 안하나?'
"""
줄 바꿈 등으로 문자열을 표현하고 싶다면, 역슬래쉬(\)를 사용할 수 있다.
'\n' # 줄바꿈
'\t' # 탭
\n 을 출력할 경우, 줄바꿈으로 표현된다.
print('파이썬\n1. 문자열\n2. 숫자열')
파이썬
1. 문자열
2. 숫자열
덧셈 표시를 이용해, 문자열끼리 합칠 수 있다.
'1 더하기 1은 ' + '2 입니다'
'1 더하기 1은 2 입니다'
곱셈 표시를 이용해, 문자열을 반복할 수 있다.
'하' * 3
'하하하'
문자열에 원하는 값을 대입할 수 있다.
예를 들어 "안녕하세요 OO님"에 "철수"를 대입할 수 있다.
문자열에 값을 여러 방법으로 대입할 수 있다.
.format()문자열 안에 { } 를 넣어준 후, .format() 을 부르고 안에 원하는 값을 넣는다.
'제 키는 {}입니다'.format('180 센치')
'제 키는 180 센치입니다.'
'제 키는 {}이며, 몸무게는 {}입니다'.format('180 센치', '비밀')
'제 키는 180 센치이며, 몸무게는 비밀입니다.'
만약 변수명으로 문자열 삽입을 원한다면 변수명을 { } 안에 넣어줄 수 있다.
height = '비밀'
weight = '180 센치'
'제 키는 {height}이며, 몸무게는 {weight}입니다'.format(height=height, weight=weight)
'제 키는 180 센치이며, 몸무게는 비밀입니다.'
f''파이썬 3.6 부터는 변수명으로 문자열 삽입을 할때 뒤에 .format() 을 붙이지 않고 f' ' 방법으로 직접 대입할 수 있다.
height = '비밀'
weight = '180 센치'
f'제 키는 {height}이며, 몸무게는 {weight}입니다'
Indexing으로 원하는 자료에 접근할 수 있다.
a = '마인드스케일' # 예시 문자열
a[0] # 문자열 첫 번째 값
'마'
a[3] # 문자열 네 번째 값
'스'
text = 'hello world' # 예시 문자열
text[0] # 문자열 첫 번째 값
'h'
text[5] # 텍스트의 여섯번째 값은,
' '
위의 예에서 보다시피, 공백도 문자열에 포함된다.
마이너스 기호는 뒤에서부터 숫자를 센다는 의미를 가지고 있다.
text[-1] # 문자열 마지막 값
'd'
text[-2] # 문자열 마지막에서 두 번째 값
'l'
Slicing으로 자료를 범위로 선택할 수 있다.
text[0:5] # 첫 다섯 자료
'hello'
text[:5] # 편의상 0 생략
'hello'
text[6:11] # 7번째 12번째 자료
'world'
text[6:] # 편의상 마지막 숫자 생략
'world'
.join() 함수는 문자열을 특정 글자로 합쳐준다.
','.join('abcd') # 'abcd' 사이에 쉼표를 추가
'a,b,c,d'
.split() 함수는 특정 글자로 문자열을 나눈다.
'사과,오렌지,포도,딸기'.split(',') # 쉼표를 기준으로 문자열 분리
['사과', '오렌지', '포도', '딸기']
.strip() 함수는 문자열 앞뒤의 공백을 지운다.
' 안녕하세요 '.strip()
'안녕하세요'
단, 문자열 내부의 빈 공백은 지우지 않는다.
' 안녕하 세요 '.strip()
'안녕하 세요'
.replace() 함수는 특정 글자를 다른 글자로 바꿔준다.
'안녕하 세요'.replace(' ', '') # 문자열 중간의 띄어쓰기 교체
'안녕하세요'
'파이쏜은 참 쉽습니다.'.replace('쏜', '썬') # '쏜'을 '썬'으로 교체
'파이썬은 참 쉽습니다.'
in을 사용해 문자열 안에 해당 글자가 포함되었는지 확인할 수 있다.
'사과' in '오늘의 메뉴는 밥, 미역국, 사과입니다.' # '사과'가 문자열에 포함되었는지 확인
True
not in을 사용해 반대로 해당 글자가 없는지 확인할 수 있다.
'사과' not in '오늘의 메뉴는 밥, 미역국, 사과입니다.' # '사과'가 문자열에 없는지 확인
False
.count() 함수는 해당 글자가 문자열에서 몇 번 쓰였는지 확인해준다.
text = 'hello world'
text.count('l') # 알파벳 'l' 이 몇번 쓰였는지 확인
3
.index() 함수는 해당 글자의 위치를 확인해준다.
text.index('e') # 'e'의 위치 확인
1
영문의 경우, 문자열을 대소문자로 바꾸는 함수들을 사용할 수 있다.
.capitalize() 함수는 문장의 첫 글자를 대문자로 바꾼다.
'hello world'.capitalize()
'Hello world'
.lower() 함수는 모든 글자를 소문자로 바꾼다.
'HeLLO woRlD'.lower()
'hello world'
.upper() 함수는 모든 글자를 대문자로 바꾼다.
'mBc, sBs, Kbs'.upper()
'MBC, SBS, KBS'
.startswith() 함수는 문자열이 해당 글자로 시작하는지 확인해준다.
'보고서 작성 중'.startswith('보고서') # 문자열이 '보고서'로 시작하는지 확인
True
.endswith() 함수는 문자열이 해당 글자로 끝나는지 확인해준다.
'보고서 작성 중'.endswith('완료') # 문자열이 '완료'로 끝나는지 확인
False
int 함수를 사용해 실수나 문자열을 정수로 만들 수 있다.
int(1.23) # 실수를 정수로 변환
1
int('123') # 문자열을 정수로 변환
123
float 함수를 사용해 정수나 문자열을 실수로 만들 수 있다.
float(5) # 정수를 실수로 변환
5.0
float('1.23') - 0.1 # 문자열을 실수로 변환
1.13
str 함수를 사용해 정수나 실수를 문자열로 만들 수 있다. 정수나 실수 이외에도 대부분의 파이썬 값은 str 함수를 이용해 문자열로 바꿀 수 있다.
str(5)
'5'
str(5.0)
'5.0'
리스트(list)는 값을 순서대로 저장하는 자료구조다.
[2, 3, 1]
[2, 3, 1]
리스트는 값들을 대괄호([])로 감싸서 만들 수 있다.
a = [1, 2, 3]
a
[1, 2, 3]
list 함수는 다른 값을 리스트 형태로 변환한다. 예를 들어 문자열을 list 함수에 넣으면 문자열이 글자들의 리스트로 바뀐다.
list('abc')
['a', 'b', 'c']
리스트 안에 몇 개의 값이 들어있는지 확인하고 싶을때는 len 함수를 사용한다.
len(a)
3
리스트 안에 있는 값을 선택하고 싶을 때는 인덱싱(indexing)을 사용한다. 인덱스는 0부터 시작한다.
x = ['a', 'b', 'c', 'd', 'e']
x[0] # x의 첫 값
'a'
x[1] # x의 두번째 값
'b'
뒤에서부터 셀 때는 마이너스 인덱스를 사용한다. -1은 뒤에서 첫번째라는 뜻이다.
x[-1] # x의 뒤에서 첫 값
'e'
리스트에서 일정 범위의 값을 선택할 때는 슬라이싱(slicing)을 사용한다. 슬라이싱은 시작 인덱스:끝 인덱스 형식을 사용하는데, 시작 인덱스의 값은 포함하지만 끝 인덱스의 값은 포함하지 않는다.
x[0:3] # 0번부터 2번까지 (3번은 포함하지 않는다)
['a', 'b', 'c']
끝 인덱스를 생략하면 시작 인덱스부터 끝까지 범위를 가리킨다.
x[3:] # 3번부터 끝까지 선택
['d', 'e']
시작 인덱스를 생략하면 처음부터 끝 인덱스 전까지 범위를 가리킨다.
x[:3] # 처음부터 2번까지
['a', 'b', 'c']
시작 인덱스:끝 인덱스:간격의 형식으로 간격을 표시할 수도 있다.
x[::2] # 처음부터 끝까지 2간격으로 선택
['b', 'd']
x[1:4:2] # 1번 값부터 4번 값 사이에서, 2간격으로 선택
['b', 'd']
간격에 음수를 사용하면 결과가 역순이 된다. 리스트의 순서를 뒤집고 싶을때는, [::-1] 을 사용한다.
x[::-1] # 리스트의 순서 뒤집기
['e', 'd', 'c', 'b', 'a']
.append() 함수는 값을 리스트에 추가한다.
x = ['a', 'b', 'c', 'd', 'e']
x.append('f') # x에 f를 추가
x
['a', 'b', 'c', 'd', 'e', 'f']
.pop() 함수는 하나의 값을 제거한다.
x.pop(-1) # 마지막 값을 제거
'f'
x # x 확인
['a', 'b', 'c', 'd', 'e']
del 함수는 슬라이싱과 인덱싱을 사용해 제거가 가능하다.
del x[-1] # 마지막 값 제거
x
['a', 'b', 'c', 'd']
del x[:2] # 첫 두개의 값 제거
x
['c', 'd']
.index() 함수는 값의 위치를 찾는다.
x = ['a', 'b', 'c', 'd', 'e']
x.index('c') # c의 위치
2
'c'를 'k'로 바꾸고 싶다면, 인덱싱을 사용한다.
x[2] = 'k' # 세 번째 값을 'k'로 지정
x
['a', 'b', 'k', 'd', 'e']
두 리스트를 더하기로 합칠 수 있다.
[1, 2, 3] + [4, 5, 6]
[1, 2, 3, 4, 5, 6]
리스트의 내용을 곱하기로 반복할 수 있다.
x = [1, 2, 3]
x * 2 # 두 번 반복
[1, 2, 3, 1, 2, 3]
튜플(tuple)은 리스트와 같이 값을 순서대로 저장하는 자료 구조다. 리스트와 달리 튜플은 수정이나 변경을 할 수 없다.
(1, 2, 3)
튜플은 위치마다 정해진 의미가 있는 경우에 사용된다. 만약 평면상에서 좌표를 두 개의 수로 나타낸다고 해보자. 이 경우 0번 값은 가로 좌표, 1번 값은 세로 좌표와 같은 식으로 정해진 의미를 가진다. 여기에 값을 추가하거나 뺄 필요가 없기 때문에 이 경우엔 튜플을 쓴다.
만약 좌표를 리스트로 나타낼 수도 있다. 하지만 프로그램이 복잡해지면 리스트는 실수로 여기에 값을 추가하거나 뺄 수도 있다. 그러면 이 좌표에 가로와 세로 좌표가 모두 들어있을지 아니면 가로 좌표만 있을지 또는 가로와 세로 외에 다른 무엇이 들어있을지 장담할 수가 없게 된다.
튜플은 형태가 고정되어 있기 때문에 좀 더 안심하고 쓸 수 있다. 또한 추가/삭제/수정 등에 대비할 필요가 없기 때문에 리스트보다 약간 더 효율적인 내부 구조를 가지고 있다.
튜플은 값들을 둥근 괄호로 감싼다.
(1, 2, 3)
(1, 2, 3)
tuple 함수에 다른 값을 넣으면 튜플로 변환한다. 예를 들어 리스트를 tuple 함수에 넣으면 튜플로 바뀐다.
tuple([1, 2, 3])
(1, 2, 3)
튜플의 인덱싱과 슬라이싱은 리스트와 같다.
x = (1, 2, 3)
x[0] # 첫번째 값 선택
1
슬라이싱을 할 경우, 값이 튜플로 반환된다.
x[0:2] # 첫 두 값 선택
(1, 2)
튜플 역시 더하기로 합치거나
y = (4, 5, 6)
x + y # 두 튜플 합치기
(1, 2, 3, 4, 5, 6)
곱하기로 반복할 수 있다.
x * 3 # 세번 반복하기
(1, 2, 3, 1, 2, 3, 1, 2, 3)
튜플의 값을 수정하려 시도하면, TypeError가 발생한다.
x[0] = 'a' # 첫 값을 'a'로 수정
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
튜플의 값을 변경하고 싶다면, 튜플을 리스트로 바꾼 후, 다시 튜플로 만드는것도 한 방법이다.
x = list(x) # 튜플을 리스트로 변환
x[0] = 'a' # 원하는 값 수정
x
['a', 2, 3]
x = tuple(x) # 리스트를 다시 튜플로 변환
('a', 2, 3)
사전(dictionary)은 키(key)와 그에 대응하는 값을 짝지워놓은 자료 구조다. 사전이 단어에 해당하는 뜻을 찾기 위한 책인 것을 생각해보면 이런 이름이 붙은 이유를 짐작할 수 있을 것이다.
사전은 키와 값을 콜론(:)으로 짝짓고, 이들을 중괄호({})로 감싸서 표시한다.
fruit = {
'사과': 10,
'딸기': 20,
}
사전을 키로 인덱싱 하면, 그에 대응하는 값을 돌려받을 수 있다.
fruit['사과']
10
기존의 값을 덮어 쓸 수도 있다.
fruit['사과'] = 100
fruit['사과']
100
사전에서는 키가 중복될 경우 먼저 나온 키는 무시한다.
repeated_dict = {'a': 1, 'a': 5, 'b': 10} # key에 'a'가 두개
repeated_dict['a']
5
사전의 키에는 숫자, 문자, 튜플 등 불변(immutable)인 값만 넣을 수 있다. 리스트처럼 가변(mutable)적인 값은 키로 사용할 수 없다.
fruit[[1]]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
사전의 키를 모두 확인하고 싶다면, .keys() 메써드를 부른다.
fruit.keys()
dict_keys(['사과', '딸기'])
사전의 값을 모두 확인하고 싶다면,.values() 메써드를 부른다.
fruit.values()
dict_values([100, 20])
사전의 키와 값을 짝지어 보고 싶다면, .items() 메써드를 부른다.
fruit.items()
dict_items([('사과', 100), ('딸기', 20)])
사전에 존재하지 않는 키를 찾으면, KeyError가 발생한다.
fruit['포도'] # 없는 key
Traceback (most recent call last)
<ipython-input-134-ad8269a8288e> in <module>()
----> 1 fruit['포도']
KeyError: '포도'
.get() 메소드는 사전에서 키로 찾아보고, 없으면 KeyError를 일으키는 대신 아무 것도 반환하지 않는다.
fruit.get('사과') # 있는 key
100
fruit.get('포도') # 없는 key
기본값을 설정하면 없는 키의 경우 기본값을 내보낸다.
fruit.get('사과', 500) # 있는 key
100
fruit.get('포도', 500) # 없는 key
500
in 으로 키가 사전에 있는지 확인할 수 있다.
'사과' in fruit
True
del 함수를 사용해 값을 삭제할 수 있다.
del fruit['사과'] # '사과' 삭제
fruit
{'바나나': 600, '오렌지': 200}
사전을 전부 비우고 싶다면, .clear() 메소드를 사용한다.
fruit.clear()
fruit
{}
집합(set)은 수학의 집합과 같은 용도로 사용한다. 집합은 반복되는 값을 허용하지 않으며, 순서 또한 보존하지 않는다.
{1, 1, 3, 2}
{1, 2, 3}
집합은 중괄호({})로 값을 감싸 만든다. 사전과 비슷하지만 키와 값의 짝이 없다는 점이 다르다.
x = {'사과', '포도', '바나나'}
x
{'사과', '포도', '바나나'}
set 함수에 다른 값을 넣으면 집합으로 변환된다. 만약 겹치는 항목이 집합에 들어있다면, 반복되는 항목은 제거된다.
set(['사과', '사과', '포도', '바나나', '바나나', '사과'])
{'바나나', '사과', '포도'}
사전을 set에 넘기면 키의 집합으로 변환된다.
집합에도 len 함수를 사용할 수 있다.
len(x)
3
집합에 값을 추가하고 싶다면 .add()를 사용한다.
x.add('오렌지') # 집합에 '오렌지' 추가
x
{'바나나', '사과', '오렌지', '포도'}
하나 이상의 값은 .update()를 사용한다.
x.update(['수박', '딸기']) # 집합에 '수박', '딸기' 추가
x
{'딸기', '바나나', '사과', '수박', '오렌지', '포도'}
값을 제거하고 싶을때는 .remove()를 사용한다.
x.remove('딸기') # '딸기' 제거
{'바나나', '사과', '수박', '오렌지', '포도'}
딸기가 제거되었다.
in 으로 값이 집합에 있는지 확인할 수 있다.
'바나나' in x
True
집합끼리 연산이 가능하다.
a = {1, 2, 3}
b = {3, 4, 5}
두 집합 사이의 합집합은 | 으로 구한다.
a | b
{1, 2, 3, 4, 5}
두 집합 사이의 교집합은 & 으로 구한다.
a & b
{3}
두 집합 사이의 차집합은 - 으로 구한다.
a - b
{1, 2}
pyautogui를 사용하면, 클릭을 자동화할 수 있다.
클릭 자동화 중, 강제 종료시키고 싶을때 단축키는 다음과 같다:
ctrl + alt + delimport pyautogui
현재 화면의 크기를 가져온다.
width, height = pyautogui.size()
width, height
(1920, 1080)
자동으로 전체화면 스크린샷을 가져와 pyautogui.png로 저장할 수 있다.
im1 = pyautogui.screenshot('pyautogui.png')
자동으로 부분 스크린샷을 가져올 수 있다. 이때 region 을 지정한다.
이를 pyautogui2.png 로 저장한다.
# region 에 left, top, width, height 형식으로 넘겨주면 된다.
im2 = pyautogui.screenshot('pyautogui2.png', region=(900, 400, 500, 500))
moveTo 를 사용하면 마우스 포인터를 자동으로 움직일 수 있다.
pyautogui.moveTo(100, 300, duration=0.5) # duration은 속도에 따라 마우스 움직임 차이
.dragTo 를 사용하면 마우스를 자동으로 드래그 할 수 있다.
pyautogui.dragTo(100, 300, duration=0.5)
현재 마우스 위치는 .position으로 알 수 있다.
pyautogui.position()
(281, 749)
현재 위치에서 마우스 포인터로 사각형을 만들어보자. 상대적인 위치는 .moveRel 을 사용한다.
pyautogui.moveRel(150, 0, duration=0.25)
pyautogui.moveRel(0, 150, duration=0.25)
pyautogui.moveRel(-150, 0, duration=0.25)
pyautogui.moveRel(0, -150, duration=0.25)
자동으로 시작버튼을 눌러보자.
pyautogui.click(24, 1065, duration=0.25)
자동으로 주피터 노트북의 저장버튼을 눌러보자.
left, top = pyautogui.position() # 저장 버튼 위에 마우스를 위치하고 코드 실행
width, height = 20, 20
저장 버튼을 스크린샷한다.
im = pyautogui.screenshot('save_btn.png', region=(left, top, width, height))
x, y, _, _ = pyautogui.locateOnScreen('save_btn.png')
pyautogui.click(x + 3, y + 4, duration=0.25)
자동으로 메모장을 띄워, 내용을 치도록 해보자. 단, 한국어는 입력이 안된다.
pyautogui.PAUSE = 0.8
pyautogui.click(24, 1065, duration=0.25) # 시작 버튼
pyautogui.typewrite('memo') # 메모장을 검색한다
pyautogui.press('enter')
# 메모장에 타자 치기
pyautogui.typewrite('Hello World !') # Hellow World! 를 적는다
pyautogui.press('enter')
pyautogui.press('enter')
pyautogui.press('hanguel') # 한/영 키보드를 누른다
pyautogui.typewrite('dkssud tptkddk !') # 안녕 세상아를 적는다
pyautogui.press('enter')
pyautogui.typewrite('안녕 세상아 !') # 단, 한국어는 인식 안됨
사용 금액을 엑셀에 자동으로 입력해보자.
import time
money = '10000'
payment = 'card' # 카드
date = '02.22.18'
pyautogui.PAUSE = 1
pyautogui.click(24, 1065, duration=0.25) # 시작 버튼 클릭
pyautogui.typewrite('excel')
pyautogui.press('enter')
time.sleep(3)
pyautogui.press('enter')
pyautogui.typewrite('amount') # 금액
pyautogui.press('tab')
pyautogui.typewrite('payment') # 결제 방법
pyautogui.press('tab')
pyautogui.typewrite('date') # 날짜
pyautogui.press('enter')
pyautogui.typewrite(money)
pyautogui.press('tab')
pyautogui.typewrite(payment)
pyautogui.press('tab')
pyautogui.typewrite(date)
pyautogui.press('enter')
데이터를 엑셀에 자동으로 입력해보자.
import pandas as pd
먼저 UCI data repository 에서 forest fires 데이터셋을 가져온다.
http://archive.ics.uci.edu/ml/datasets/Forest+Fires
df = pd.read_csv('forestfires.csv')
df = df[:10]
pyautogui.click(24, 1065, duration=0.25) # 시작버튼 클릭
pyautogui.typewrite('excel')
pyautogui.press('enter')
time.sleep(3)
pyautogui.press('enter')
pyautogui.PAUSE = 0.3
pyautogui.typewrite('X') # 엑셀에 작성
pyautogui.press('tab')
pyautogui.typewrite('temperature')
pyautogui.press('enter')
# 데이터에서 X 와 temp 항목을 입력한다
for num, i in df.iterrows():
pyautogui.typewrite(str(i['X']))
pyautogui.press('tab')
pyautogui.typewrite(str(i['temp']))
pyautogui.press('enter')
for 문은 자료형에 들어있는 각 항목마다, 같은 코드를 반복할때 사용한다.
for <항목> in <자료>:
<항목에 대한 코드>
예를 들어, 새해가 되어 각 사람들의 나이에 1 씩 더해야 할 때, for 문을 사용해 1을 더해주면, 코드가 더욱 간편해진다.
for age in [13, 17, 22]: # 세 명의 사람의 나이가 13, 17 22 라고 할 때,
print(age + 1) # 세명 각각의 나이에 차례대로 1 씩 더한다.
14
18
23
여기서 for문안에 사용되는 age 는 리스트 안의 항목을 지칭하기 위한 일시적인 변수 이름이다.
age 를 실행시켜보면, for문의 마지막 값이 저장되어있다.
age
22
만약 for문 전에 age = 100 이라고 변수를 지정해준다면, for 문에서 사용되는 age에 의해 덮어쓰여진다.
age = 100
for age in [13, 17, 22]:
print(age + 1)
14
18
23
예제를 통해 더 자세히 알아보자.
name 이라는 변수에 과일 이름이 리스트로 들어있다.
name = ['사과', '포도', '딸기']
name 을 for 문에 넘겨준다.
for 문 다음의 문장은 들여쓰기를 해줘야한다.
for i in name: # 'name' 의 각 항목을 i 라고 지정한 후,
print(i) # 들여쓰기가 되어있는 상태에서, i 를 출력한다.
'사과'
'포도'
'딸기'
name 안에 들어있는 항목들이 처음부터 끝까지 i 라는 변수에 대입이 되어 print 라는 코드를 실행하여 출력이 된다.
for 문을 사용할 때, 자주 사용되는 range 내장 함수에 대해서 알아보자.
range 는 말 그대로 범위를 뜻 한다.
range(5) # range 5 를 실행시킨다.
range(0, 5)
결과로, range 자료형이 만들어진다.
이번엔, range 를 for 문에 적용하여 결과를 확인하자.
for i in range(5): # range 를 이해하기 쉽게, for 문으로 돌려,
print(i) # 각 값을 출력한다.
0
1
2
3
4
결과를 확인하면, 0 부터 4 까지의 숫자가 출력된다.
이렇게 레인지는 0 부터 지정된 숫자까지 정수값들을 만든다.
이번에는 range 의 시작 숫자도 지정하자.
for i in range(1, 5): # range 1 부터 5 까지 for 문으로 돌려,
print(i) # i 를 출력하면,
1
2
3
4
1 부터 4 까지의 숫자가 나온다.
이렇게 range 에 시작 숫자도 지정할 수 있습니다.
range 함수를 통해 숫자를 일정 간격으로 셀 수도 있다.
range(0, 10, 2) # 0 부터 10까지의 수를 2 간격으로 세는 range 함수
for 문을 통해 확인하자.
for i in range(0, 10, 2): # for 문으로 0 부터 9 까지, 2 간격으로
print(i) # i 를 출력한다.
0
2
4
6
8
0 에서부터 9 까지 2 간격으로 숫자가 출력된다.
이번에는 for 문을 사용해, 1부터 10 까지 더해보자.
먼저 합을 기록해놓을 total 이라는 변수를 0 이라고 저장한다.
total = 0
for i in range(1, 11): # 1 부터 10 까지 숫자를 i 라고 지정한 후,
total = total + i # total 에 i 를 더해주고, 새로 total 이라고 저장한다.
print(i, total) # i 와 total 을 출력한다.
1 1
2 3
3 6
4 10
5 15
6 21
7 28
8 36
9 45
10 55
i 가 1 씩 증가하고, total 은 i 만큼 증가하는 것을 확인할 수 있다.
total 을 확인해보니 1부터 10 까지의 합인 55인 것을 알 수 있다.
total
55
for 문을enumerate 와 같이 사용하면, 각 항목의 값과 인덱스 값을 동시에 알 수 있다.
예시를 통해 더 자세히 알아보자.
name = ['사과', '포도', '딸기']
for i, j in enumerate(name):
print(i, j)
0 사과
1 포도
2 딸기
i 부분은 j 항목의 인덱스 값으로 출력된다.
만약 인덱스의 시작값을 바꾸고 싶다면, 원하는 시작값을 넘겨줄 수 있다.
예시로 인덱스값을 1 로 시작하게 바꿔보자.
name = ['사과', '포도', '딸기']
for i, j in enumerate(name, 1):
print(i, j)
1 사과
2 포도
3 딸기
for 문을 사용한 반복 범위는 들여쓰기 내에만 해당된다.
a, b, c 를 출력해보자.
print('a')
print('b')
print('c')
a
b
c
for 문의 들여쓰기에 'a' 를 출력하도록 코드를 짜고 결과물을 예상해보자.
for i in range(3):
print('a')
print('b')
print('c')
들여쓰기에 해당하는 'a' 만 반복되어 나온다.
a
a
a
b
c
이번에는 for 문의 들여쓰기에 'a' 와 'b'를 출력하도록 코드를 짜고 결과물을 예상해보자.
for i in range(3):
print('a')
print('b')
print('c')
들여쓰기에 포함된 'a' 와 'b' 만 반복되고 들여쓰기 밖에 있는 'c' 는 반복되지 않는다.
a
b
a
b
a
b
c
이번에는 a, b, c 와 해당 숫자를 확인해보자.
for i in range(3):
print(i)
print('a')
print('b')
print('c')
각 반복 번호와 반복되는 내용을 확인할 수 있다.
0
a
b
c
1
a
b
c
2
a
b
c
이번에는 반복 번호와 'a' 만 출력해서 들여쓰기에 구역에 넣어보자.
for i in range(3):
print(i)
print('a')
print('b')
print('c')
'a' 만 3 번 반복되는 모습을 볼 수 있다.
0
a
1
a
2
a
b
c
만약 들여쓰기를 해주지 않으면 IndentationError 를 보게 된다.
for i in range(3):
print(i)
print('a')
print('b')
print('c')
File "<ipython-input-8-f681578deac8>", line 2
print(i)
^
IndentationError: expected an indented block
for 문을 if 문과 같이 사용할 수 있다.
for x in range(3):
if x % 3 == 0: # x 가 3의 배수일 경우
print('a')
elif x % 3 == 1: # x 가 3으로 나눴을 경우 나머지가 1 일 때
print('b')
else: # x 가 3으로 나눴을 경우 나머지가 2 일 때
print('c')
a
b
c
for 문 안에 for 문을 넣을 수도 있다.
for a in range(1, 4): # range 1 부터 4까지를 for 문에 넘겨주고,
for b in range(1, 4): # 또 다른 for 문을 입력한다.
print('a-', a, 'b-', b) # 첫 for 문의 항목 a 와 두번째 항목인 b 를, 짝지어서 출력
a- 1 b- 1
a- 1 b- 2
a- 1 b- 3
a- 2 b- 1
a- 2 b- 2
a- 2 b- 3
a- 3 b- 1
a- 3 b- 2
a- 3 b- 3
a 가 1일 때, b 는 1 에서 3 까지 돈다.
그 후에 a 가 2 로 넘어가고, b 는 다시 1 에서 3 까지 돈다.
이번에는 for 문 안에 for 문을 사용해, 구구단을 만들어보자.
예를 들어, 아래와 같이 2 곱하기 1이 있다면,
2 * 1 # 첫번째 포문은 왼쪽 숫자를, 두번째 포문은 오른쪽 숫자를 나타내도록 한다.
첫 번째 for 문은 2 단부터 9 단 까지 수행하기 위해, 바깥 for 문에 range 2와 10 을 넘겨주고 각 항목을 i 라고 부른다.
안쪽 for 문에는 range 1 과 10 을 넘겨주고, 각 항목을 j 라고 부른다.
for i in range(2, 10):
for j in range(1, 10):
print(i * j) # i 와 j 의 곱을 출력한다.
print('------') # 큰 포문이 하나 끝날때마다 구분선을 그어주도록 한다.
2단 부터 9 단 까지의 구구단이 출력된다.
for i in tuple((1, 2, 3)): # for 문에는 list 외에도, tuple 이나,
for j in set((1, 2, 3)): # set 등을 넘겨줄 수도 있다.
print(i, j)
1 1
1 2
1 3
2 1
2 2
2 3
3 1
3 2
3 3
dictionary 의 경우, dictionary.keys() 등의 methods 를 활용해, for 문을 돌 수 있다.
for i in dict(a=1, b=2, c=3).keys():
print(i)
a
b
c
이번에는 for 문 안에 이프문을 사용해 홀수와 짝수를 다르게 출력하는 코드를 만든다.
for i in range(1, 11): # 1 에서 11 전 까지 for 문을 돌려, 각 항목을 i 라고 지정한다.
if i % 2 == 0: # 이프문을 사용해 만약 i 를 2 로 나눌 때, 나머지가 0 이면,
print(i, '- 짝수입니다.') # i 와 '짝수입니다' 를 출력한다.
else: # i 를 2로 나눌 때, 나머지가 0 이 아니라면,
print(i, '- 홀수입니다.') # i 와 '홀수입니다' 를 출력한다.
1 - 홀수입니다.
2 - 짝수입니다.
3 - 홀수입니다.
4 - 짝수입니다.
5 - 홀수입니다.
6 - 짝수입니다.
7 - 홀수입니다.
8 - 짝수입니다.
9 - 홀수입니다.
10 - 짝수입니다.
1 에서 10 까지 홀수와 짝수인지가 출력되었다.
python 에서 조건에 따라 다른 코드를 실행하고 싶을때, if 문을 사용한다.
if 문은 기본적으로 조건문과 실행할 블록으로 이루어져 있다.
if 문 안에 들어있는 블록은 조건문이 참일 경우에만 실행됩니다.
if <조건문>:
<블록>
예시로 자세하게 알아보자.
a = True
if a:
print('a is true')
a is true
위 코드를 실행하면, "a is true" 라는 문구가 출력된다.
반대로 b에 False 를 넣어주면,
b = False
if b:
print('b is true')
b 가 참이 아니기 때문에, 아무것도 출력되지 않는다.
이번엔 숫자로 된 예시를 확인하자.
a = 10
if a > 5:
print('large')
large
a 가 5 보다 크기 때문에 'large' 가 출력이 된다.
b = 2
if b > 5:
print('large')
'b > 5' 의 결과가 False 이기 때문에, 아무것도 출력이 되지 않는다.
만약 조건문이 충족되지 않았을 경우, 다른 코드를 실행시키고 싶다면, else 를 사용하면 된다.
if <조건문>:
<참일 경우>
else:
<거짓일 경우>
이전의 예시를 다시 살펴보자.
b = 2
if b > 5: # b 가 5보다 크면,
print('large') # 라지를 출력하고,
else: # 크지 않으면,
print('small') # 스몰을 출력하게 합니다.
small
if 문에 조건에 충족되지 않았기 때문에, else 문에 있는 print('small') 코드가 실행 되었다.
또다른 예시를 살펴보자.
x 가 3의 배수이면 'a' 를 출력하도록 if 문을 짜보자.
x = 3
if x % 3 == 0:
print('a')
else:
print('d')
x 의 값이 3 이기 때문에 a 가 출력된다.
a
if 문은 하나의 조건일 경우에만 사용할 수 있었다.
만약 또 다른 조건에 따라 블록을 지정하고 싶다면, elif 문을 사용하면 된다.
elif 는 else if 의 줄임말이다.
if <조건문>:
<참일 경우>
elif <또 다른 조건문>:
<또 다른 조건문이 참일 경우>
i 가 2 일 경우는 'two two', 3 일 경우는 'three three' 를 출력하는 코드를 생성해보자.
i = 3
if i == 2:
print('two two')
elif i == 3:
print('three three')
else:
print('.')
three three
i 가 3 일경우 elif 부분이 실행되어서 'three three' 가 출력되었다.
elif는 여러개 사용할 수 있다.
if 문 연습을 위해 나이에 따라 호칭을 지정하는 프로그램을 만들어보자.
age = 28 # 예를 들어 어떤 사람의 나이가 28세 라고 치자.
if age < 20:
print('미성년')
elif age < 40:
print('청년')
elif age < 65: # 또 다른 elif
print('중년')
else:
print('노인')
청년
if 문을 돌려보면, 나이 28 은 청년으로 불리게 된다.
if 문 안에, if 문을 중복해서 사용할 수 있다.
이전과 같은 호칭을 지정하는 프로그램에서 성별 옵션도 추가하자.
나이가 65세 이상인데, 남성이면 '할아버지', 여성이면 '할머니'로 결과를 출력하도록 하자.
age = 80 # 예를 들어 어떤 사람의 나이가 80세 이고,
gender = 'male' # 성별은 '남성' 이라고 치자.
if age < 20:
print('미성년')
elif age < 40:
print('청년')
elif age < 65:
print('중년')
else: # else 문이 실행되면, 그 안의 if 문이 실행된다.
<작성해야할 부분>
할아버지
실행결과, 나이가 80 이고 성별이 남성이면 할아버지라고 불리게 된다.
python 에 불리언 (Boolean) 자료형이 있다. 불리언 (Boolean) 이란 참과 거짓, 두가지 값만 가지는 자료형을 의미한다.
a = True # a 를 트루라고 저장한다.
a # a 를 실행시켜보면,
True
참을 내보낸다.
type(a)
bool
a 의 타입을 살펴보면, 불리언이라고 출력된다.
불리언은 특히, 비교연산에서 사용한다.
python 에서 좌우 항목을 비교하기 위해, 여러가지 비교연산자를 허용한다.
<항목A> <비교연산자> <항목B>
이번 강의에서는, 비교 연산자에 대해 알아보자.
좌 우의 값이 동일하다 는 표현을 사용하고 싶을 때는, = 표시를 두 개 사용한다.
2 와 2 의 값이 같다 를 python 으로 표현해보자.
2 == 2
True
실행시켜본 결과, 참인것을 알 수 있다.
좌우의 값이 같지 않다는 표현을 사용하고 싶을 때는 != 표시를 사용한다 .
2 != 4
True
2 와 4가 같지 않다는 것을 의미하는 2 != 4를 실행시키면, 결과가 참으로 출력된다.
값이 더 크거나 더 작다를 표현하고 싶을때는<,> 를 사용한다.
2 < 4
True
2 > 4
False
값을 포함해, 크거나 같다를 표현하고 싶을때 <, > 와 함께 = 를 사용한다.
2 <= 2
True
1 >= 2
False
숫자형 데이터 외에도, 비교연산을 문자열이나 리스트 등에도 적용시킬 수 있다.
'sam' == 'sam'
True
리스트 끼리 비교해보자.
a = [1, 2, 3]
b = [1, 2, 3]
a == b
True
비교할때, 좌우 데이터형이 같아야한다.
3 >= [1, 2]
TypeError Traceback (most recent call last)
<ipython-input-18-d947d032a41d> in <module>()
----> 1 3 >= [1, 2]
TypeError: '>=' not supported between instances of 'int' and 'list'
예를 들어, 숫자 3 이 1과 2 가 들어있는 리스트 보다 크거나 같다고 표현해보면, integer 와 list 를 비교할 수 없다는 TypeError 가 발생한다.
python 에서는 영어 문장에서 쓰이는 and, or, not 과 같은 논리연산이 그대로 쓰인다.
and, or, not
이 강의에는 and, or, not 을 다룬다.
and 는 좌우 항목이 모두 True 일 경우에만, True 라는 결과를 갖는다.
<항목A> and <항목B>
예시를 한번 확인해보자.
'sam' == 'sam' and 8 / 2 == 4
True
'sam' 과 또 다른 'sam' 이 같기 때문에 and 의 왼쪽 항목은 True 이며,
8 / 2 는 4 와 같기 때문에, and 의 오른쪽 항목도 True 이다.
and 의, 좌우가 모두 True 이기 때문에, 결과가 True 이다.
이번에는 'sam' 을 'cam' 으로 바꿔서 실행시켜 보자.
'cam' == 'sam' and 8 / 2 == 4
False
결과는 False 로 출력된다.
and 의 왼쪽과 오른쪽 항목 모두 True 일 경우에만 and 의 값이 True인 것을 확인할 수 있다.
or 는 왼쪽과 오른쪽 항목 중, 하나만 True 여도 True 값을 갖는다.
<항목A> or <항목B>
('cam' == 'sam') or (8 / 2 == 4)
True
or 양 옆의 두 항목 중, 8 / 2 == 4 라는 항목 하나가 True 이기 때문에 True 로 출력된다.
왼쪽과 오른쪽 항목 모두 False 인 경우를 제외하고는, 결과값이 모두 True 로 나온다.
not 은 무엇이 아니다 라는 부정의 의미를 갖고 있다. 참인 값을 거짓으로 뒤집어주고, 거짓인 값을 참으로 뒤집어준다.
not
1 == 3
False
1 과 3은 같지 않기 때문에 결과가 False 이다.
하지만 1 은 3 이 아니다 라고 not 을 써주면,
not (1 == 3)
True
True 값을 갖는다.
not 은 등호가 필요한 비교 연산에는 사용할 수 없다.
1 not > 2
File "<ipython-input-90-ebf613fb3e62>", line 1
1 not > 2
^
SyntaxError: invalid syntax
in 은 어디 안에 들어있다 는 뜻이다.
예를 들어, 4 가 리스트 안에 들어있다 고 써주면,
4 in [1, 2, 3]
False
4 가 리스트 안에 없기 때문에, 결과가 False 로 출력된다.
not 을 in 과 함께 쓸 수도 있다.
in 앞에 not 을 붙여주면 들어있지 않다 는 것이다.
4 not in [1, 2, 3]
True
이번엔 결과가 True 로 나온다.
while 문은 어느 조건이 '참' 일 동안, 블록을 반복한다.
while <조건>: # 조건이 참 이라면
<블록> # 블록내에 코드를 실행한다.
예시로 while 문을 이용해 0 에서 4 까지 출력하도록 하자.
while 문은 조건이 참일동안 계속 돌아간다. 따라서, for 문과 달리 무한 반복에 빠지지 않도록 주의해야한다.
i = 0 # while 문을 몇 회 도는지 추적하기 위해, while 문 밖에 i 는 0 이라고 지정한다.
while i < 5: # i 가 5 보다 작을 때까지, while 문을 돈다.
print(i) # 반복마다 현재 i 를 프린트 한다.
i = i + 1 # 다음 반복문으로 넘어가기 전, i 에 1 을 더해준다.
# 이 단계를 생략하면, i가 계속 0 이기 때문에, 무한 반복을 하게 된다.
0
1
2
3
4
실행 결과, 0 에서 4 까지 숫자가 출력된다.
이번에는 while 문을 이용해 0 에서 4 까지, 더하는 코드를 작성해보자.
total = 0 # 먼저 합계를 할당할 변수, total 을 만든다.
i = 0 # 다음으로 i 에 0 을 할당한다.
while i < 5: # i 가 5 보다 작을 동안,
total = total + i # total 에 i 를 더하고, 결과를 total 이라고 저장한다.
print(i, total)
i = i + 1
0 0
1 1
2 3
3 6
4 10
실행결과, i 가 커질때마다, i 가 기존 숫자에 더해진다.
total # total 을 확인해보니,
10
0 에서 4 까지 합이 구해졌다.
while 문 내부에 while 문을 또 사용할 수 있다.
1 단부터 5 단까지, 구구단을 외는 프로그램을 짜보자.
구구단의 왼쪽 숫자를 담당할 a 라는 변수에 1 이라는 시작값을 넣는다. a 는 while 문 밖에 지정해야하한다.
a = 1
while a < 10: # 9 단까지만 보기 위해, a가 10 보다 작을 동안 와일문을 돌게 한다.
b = 1 # 구구단의 오른쪽 숫자인 b 라는 변수에 1 이라는 시작값을 넣는다.
while b < 10: # b 가 10보다 작을 때,
print(a * b) # a 와 b 의 곱을 print 한다.
b = b + 1 # b 에 1 을 더해준다.
print('---') # 작은 while 문이 끝날때마다 구분선을 그어준다.
a = a + 1 # 작은 while 문을 돈 후, a 에 1 을 더해준다.
실행결과, 구구단이 9단까지 출력된다.
while 문의 조건은 숫자가 아니여도 된다. True/False 를 이용해, while 문을 작성해보자.
우선 five 라는 변수에 False 라고 지정을 한다.
그리고 i 는 0 이라고 지정한다.
five = False
i = 0
while not five: # while 이 참이 아닐 동안,
print(i) # i 를 프린트 하고,
i = i + 1 # i 에 1 을 더해준다.
if i == 5: # 만약 i 가 5 와 같다면,
five = True # five 는 True 라고 지정한다.
0
1
2
3
4
실행결과, 0 에서 4 까지 숫자가 프린트 되었고,
i 가 5 가 되자, five 는 True 가 되어, while 문에서 빠져나왔다.
while 조건을 True 로 놓을 경우, 무한으로 while 루프를 돌 수 있다.
while 문이 도는 동안, hi 를 프린트 하는 코드를 실행시켜보자.
while True:
print('hi')
hi
hi
hi
hi
...
그 결과, 주피터 노트북에서 무한으로 'hi' 를 프린트하게 된다.
무한 루프를 강제로 끊기 위해, 커맨드 창에서 ctrl + c, 를 누르거나, 상단에 멈춤 표시를 눌러 강제로 루프를 빠져나와야 한다.
따라서 while 문을 사용할 때, 무한루프에 빠지지 않도록 조심해야한다.
break 문은 반복문에서 중간에 강제로 빠져나올 때 사용한다.
for 문에서 break 를 사용해 반복문을 빠져나오도록 하자.
0 에서 99 까지 도는 for 문을 생성한다면 for 문이 100번 돌아야한다.
하지만, 중간에 i 가 5 일 때, break 를 넣어주자.
for i in range(100):
print(i)
if i == 5:
break
그 결과, i 가 5 가 되자 중간에 for 문을 멈추고 빠져나온다.
0
1
2
3
4
5
while 에서도 break 를 사용할 수 있다.
i = 0 # i 는 0 이라고 지정한 후 ,
while True: # 무한 루프
print(i) # i 마다 프린트를 한다.
if i == 5: # 만약, i 가 5 와 같다면,
break # break 를 실행시킨다.
i = i + 1 # 한 루프마다 i 를 1 씩 증가시킨다.
그 결과, i 가 5 가 되자 while 문을 멈추고, 빠져나왔다.
0
1
2
3
4
5
continue 문은 반복을 넘어갈때 사용한다.
for 문에서 continue 를 사용해 반복문을 빠져나오도록 하자.
0 에서 9 까지 도는 for 문을 돈 후, 숫자에 100 을 곱해 출력하되,
짝수인 경우 continue 를 사용해보자.
for i in range(10):
if i % 2 == 0:
continue
a = i * 100
print(a)
실행결과, 짝수인 경우 continue 이하 코드가 실행되지 않고 다음 숫자로 넘어갔다.
100
300
500
700
900
while 에서도 continue 를 사용할 수 있다.
i = 0 # i 는 0 이라고 지정한 후 ,
while i < 10: # 9 까지의 수
i = i + 1 # 한 루프마다 i 를 1 씩 증가시킨다.
if i % 2 == 0: # 만약, i 가 짝수라면,
continue # continue 를 실행시킨다.
a = i * 100
print(a)
실행결과, 짝수인 경우 continue 이하 코드가 실행되지 않고 다음 숫자로 넘어갔다.
100
300
500
700
900
pass 문은 어떠한 실행도 하지 않고 넘어가고 싶을 때, 문법을 채우기 위해 사용한다.
continue 에서 사용했던 for 문에 pass 문을 넣어보자. 0에서 9까지 숫자 중 짝수를 만나면 pass 한 후, 숫자에 100을 곱해 출력해보자.
for i in range(10):
if i % 2 == 0:
pass
a = i * 100
print(a)
for 문에서 continue 를 사용했을 경우 홀수만 출력되었지만 pass 를 사용하니 모든 숫자가 출력되었다. 즉, pass 는 코드에 영향은 없지만 문법을 채우고 싶을때 사용할 수 있다.
0
100
200
300
400
500
600
700
800
900
pass 문은 함수에 구체적인 코드는 짜지 않고 일단 실행보고 싶을때 손쉽게사용할 수 있다.
def some_func():
pass
파이썬에서 파일을 생성할 수 있다.
예시로, sample.txt 파일을 만들어보자.
f = open('sample.txt', 'w') # 'sample.txt' 파일을 'w'(작성) 모드로 열기
f.close() # 파일 닫기
폴더를 확인해보니, 빈 파일이 생겼다.
방금 작성한 코드를 조금 더 자세히 살펴보자.
open 함수는 파일을 열고 닫을 때 사용하는 함수다.
open 안에, 다루고자 하는 파일 이름을 확장자 까지 적어준 후 (sample.txt), 그 옆에는 열기 모드를 적는다.
파일을 새로 작성하고 싶을 때, write의 약자인, w 모드로 파일을 연다.
f = open('sample.txt', 'w') # write 모드
같은 이름으로 존재하는 파일을 w 로 불러온다면, 기존 자료가 덮어쓰이니 주의해야 한다.
open으로 파일을 열었으면 파일을 닫아야 한다.
f.close() # 파일 닫기
이미 존재하는 파일을 불러와, 읽기만 하고싶을 때는, read 의 r 모드로 연다.
f = open('sample.txt', 'r') # read 모드
파이썬3는 파일을 저장할 때, 텍스트 모드 혹은 바이너리 모드로 저장한다.
w 가 아닌 wb 로 작성하면, byte로 파일이 작성된다.
f = open('sample_binary.txt', 'wb') # write binary 모드
텍스트가 아닌 byte파일을 열때는, r 이 아닌 rb 로 열어야 한다.
f = open('sample_binary.txt', 'rb') # read binary 모드
파일에 내용을 작성하고 싶을때는 .write() 메써드를 사용한다.
f = open('sample.txt', 'w') # write 모드
f.write('Hello.\n')
f.write('Nice to meet you.\n')
f.write('This is another line.\n')
작성 후 파일을 닫는다.
f.close()
실행 폴더에서 파일을 확인한다.
내용이 들어와있다.
작성한 파일을 불러보자.
f = open('sample.txt', 'r') # read 모드
.read() 메써드를 사용해 내용을 프린트 해보자.
print(f.read())
Hello.
My name is Mind Scale.
This is another line.
내용이 불려왔다.
.readlines() 메써드를 사용하면, 줄바꿈 단위로 문장이 쪼개져서 불려온다.
f = open('sample.txt', 'r')
print(f.readlines())
['Hello.\n', 'My name is Mind Scale.\n', 'This is another line.\n']
for 문을 사용해, 내용에 접근할 수 있다.
f = open('sample.txt', 'r')
for line in f:
print(line)
print('----') # 구분선
Hello.
----
My name is Mind Scale.
----
This is another line.
----
파일을 불러준 후, 항상 .close()로 닫아줘야 한다.
매번 .close()를 하기에 번거롭기 때문에, 수행 후에 자동으로 파일을 닫아주는 with를 같이 사용하는 것을 추천한다.
with open('sample.txt', 'r') as f:
for line in f:
print(line)
Hello.
My name is Mind Scale.
This is another line.
한국어 텍스트를 다룰때 인코딩 문제가 자주 발생한다.
윈도우즈 운영체제의 기본 인코딩 방식은 CP949 이지만,
리눅스나 macOS 에서는 기본 인코딩 방식이 UTF-8 이다.
따라서 윈도우즈에서 한글로 작성한 파일이 간혹 다른 운영체제에서 깨져서 나타날 수 있다.
통상적으로 기본 인코딩을 UTF-8 으로 통일한다.
따라서 자료를 작성할때는 UTF-8 로 저장하고, 열때는 인코딩을 지정하는 것을 잊지말자.
with open('sample_korean.txt', 'w', encoding='utf8') as f: # 인코딩 지정
f.write('안녕.\n')
with open('sample_korean.txt', 'r', encoding='utf8') as f: # 인코딩 지정
print(f.read())
안녕.
만약, UTF-8으로 인코딩 된 파일을 CP949로 열려고 한다면, UnicodeDecodeError가 발생한다.
with open('sample_korean.txt', 'r', encoding='cp949') as f:
print(f.read())
UnicodeDecodeError Traceback (most recent call last)
<ipython-input-25-6657879b15e9> in <module>()
1 with open('sample.txt', 'r', encoding='cp949') as f:
----> 2 for line in f:
3 print(line)
UnicodeDecodeError: 'cp949' codec can't decode byte 0xec in position 0: illegal...
이미 존재하는 파일에, 내용을 추가하고 싶다면 append('a') 모드를 사용한다.
with open('sample_korean.txt', 'a', encoding='utf8') as f: # append 모드
f.write('This line is added.') # 추가할 내용 작성
파일을 불러와 추가 된 문장을 확인해보자.
with open('sample_korean.txt', 'r', encoding='utf8') as f: # read 모드
print(f.read())
안녕.
This line is added.
os 라이브러리는 운영체제 관련 기능을 수행할 수 있다. os 는 operating system 의 약자이다.
import os
현재 작업 디렉토리를 알고 싶다면 getcwd() 메써드를 사용하면 된다. 윈도에서는 디렉토리를 '폴더(folder)'라고 부른다.
os.getcwd()
'C:\\Users\\user\\Documents'
현재 자신의 작업 위치가 나온다.
현재 작업 디렉토리의 있는 파일과 디렉토리 목록을 보고싶다면, listdir() 함수를 사용한다.
os.listdir()
['.ipynb_checkpoints',
'.RData',
'.Rhistory',
'46E8.tmp',
'7895.tmp',
...]
현재 경로에 존재하는 파일과 디렉토리가 리스트 형태로 출력된다.
만약, 다른 경로에 존재하는 파일과 디렉토리 목록이 알고 싶다면, listdir()에 경로를 넘겨주면 된다.
os.listdir('C:/Users/user/Anaconda3') # Anaconda3 디렉토리에 들어있는 파일 목록을 확인
['.nonadmin',
'api-ms-win-core-console-l1-1-0.dll',
'api-ms-win-core-datetime-l1-1-0.dll',
'api-ms-win-core-debug-l1-1-0.dll',
'api-ms-win-core-errorhandling-l1-1-0.dll',
'api-ms-win-core-file-l1-1-0.dll',
...]
rename()을 사용해 파일 이름을 변경할 수 있다.
os.rename() 에 현재 파일 이름과 새 파일 이름을 넘겨주면 이름이 변경이 된다. 먼저 '연습.txt'라는 빈 파일을 만들어 보자.
f = open('연습.txt', 'w')
f.close()
os.listdir()로 잘 생성되었는지 확인한다.
os.listdir()
['연습.txt']
이제 "연습"을 "os연습"이라고 변경한다.
os.rename('연습.txt', 'os연습.txt')
os.listdir()
['os연습.txt']
이제 "연습.txt"가 "os연습.txt"로 변경되었다.
remove()를 통해 파일을 삭제할 수 있다. 앞서 만든 OS연습.txt를 삭제해보자.
os.remove('OS연습.txt')
os.listdir()
[]
os 의 mkdir()를 통해 디렉토리를 생성할 수 있다.
생성하고자 하는 디렉토리 이름을 os.mkdir()에 넘겨주면 된다. os.mkdir()를 통해 '파이썬'이라는 디렉토리를 생성해보자.
os.mkdir('파이썬') # '파이썬'이라는 디렉토리 생성
os.listdir()를 통해 디렉토리가 잘 생성되었는지 확인해보자.
os.listdir() # 현재 작업 위치에서의 파일 목록들
['파이썬']
현재 작업디렉토리를 변경하고자 한다면, chdir()를 사용하면 된다.
이는 불러오고 싶은 파일이 다른 경로에 있거나, 저장하고 싶은 디렉토리가 현재 작업 디렉토리와 다른 경우에 사용된다.
os.chdir에 변경하고 싶은 디렉토리를 넘겨주면 된다.
os.chdir(<변경하고 싶은 작업 디렉토리>)
os.getcwd()
'C:\\Users\\user\\Documents'
현재의 작업디렉토리는 user/Documents 라는 디렉토리이다.
아래 코드를 실행시키고, 다시 현재 작업디렉토리를 확인하자.
os.chdir('../')
os.getcwd()
'C:\\Users\\user'
..은 현재 디렉토리의 상위 디렉토리를 의미한다. 작업디렉토리가 이전과 같이 Documents 가 아니라 상위디렉토리인 user 로 변경된 것을 확인할 수 있다.
'../' 를 사용하면 상위 디렉토리 내에 있는 디렉토리나 파일로 접근이 가능하다.
os.chdir('../파이썬') # 현재의 상위디렉토리에 있는 '파이썬' 이라는 디렉토리로 작업디렉토리 변경
위에서 생성한 파이썬이라는 파일로 디렉토리를 변경해보자.
os.chdir('C:/Users/user/Documents/파이썬') # 파이썬이라는 디렉토리로 작업위치 변경
변경 되었는지 확인하기 위해, os.getcwd()를 사용하자.
os.getcwd()
'C:\\Users\\user\\Documents\\파이썬'
현재 작업 디렉토리가 파이썬 이라는 디렉토리로 변경 되었다.
파일이 아닌 디렉토리를 삭제하고 싶을 경우, rmdir()를 사용한다. 전에 만들어놓은 "파이썬" 디렉토리를 삭제해보도록 하자.
os.rmdir('파이썬')
os.listdir()
[]
현재 디렉토리를 확인하면 디렉토리가 삭제된 것을 알 수 있다.
os의 하위 모듈인 os.path에는 파일과 디렉토리의 여러 가지 추가 정보를 확인할 수 있는 함수들이 있다.
os.path.exists()로 파일이나 디렉토리의 존재 유무를 알 수 있다.
현재 디렉토리에 "python" 라는 디렉토리가 있는지 확인한다.
os.path.exists('python')
False
사용자가 입력한 경로가 파일인지 디렉토리인지 궁금할땐, .isdir() 혹은 .isfile()을 사용하면 된다.
os.mkdir('python')
먼저 python이라는 디렉토리를 만들고 'python'이라는 경로가 디렉토리인지 확인한다.
os.path.isdir('python')
True
python 은 디렉토리가 맞다는 결과가 출력이 되었다.
이번엔 is.file() 에 "python"을 넘겨주자.
os.path.isfile('python')
False
False 라는 결과가 나온다.
os.path.getmtime() 을 사용하면, 파일의 수정된 시간 등을 구할 수 있다.
os.path.getmtime('sales.csv')
1516758099.6377094
결과는 유닉스 시간(UNIX time)이라는 방식으로 표현된다. 일반적인 시간 단위는 60초 = 1분, 24시간 = 1일 등으로 십진법을 따르지 않기 때문에 계산이 불편하다. 유닉스 시간은 모든 시간을 초단위로만 나타낸다. 유닉스 시간으로 0초는 1970-01-01 00:00:00이다. 위의 예에서 sales.csv가 생성된 시간은 1516758099초로 2018년 1월 24일에 해당한다. 시간을 다루는 구체적인 방법은 다음에 알아보도록 하자.
현재 작업 폴더에서 확장자가 .txt인 모든 파일의 내용을 출력하는 프로그램을 작성하라.
크롤링을 할 때, 날짜를 기준으로 자료를 가져와야 하는 경우들이 있다.
이번 강의에서는 파이썬에서 날짜를 쉽게 처리하는 방법을 살펴보도록 하자.
이번 강의에서는 몇 가지 기능에 대해서만 알아본다.
더 많은 기능들은 공식문서에서 확인할 수 있다.
http://arrow.readthedocs.io/en/latest/
날짜를 처리할 땐 arrow 라는 라이브러리를 사용한다.
pip install arrow 를 통해 설치를 할 수 있다.
import arrow
arrow.now()를 이용하여 현재시간을 알 수 있다.
arrow.now()
<Arrow [2018-01-04T18:10:27.713067+09:00]>
현재 시간은 2018년 1월 4일 오후 6시 10분이 나온다.
다른 장소의 현지 시간을 구할수도 있다.
arrow.now('US/Pacific') # 미태평양시간의 현재시간을 구한다.
<Arrow [2018-01-04T10:10:27-08:00]>
arrow.utcnow() # 협정 세계시인 utc 기준으로, 현재 시간을 구한다.
<Arrow [2018-01-04T09:10:53.289173+00:00]>
그렇다면 utc 시간으로 현재 시간이 나온다.
날짜를 arrow형태로 받고 싶다면, arrow.get()을 사용하면 된다.
arrow 형태로 날짜를 받으면, 날짜를 다룰 수 있다.
arrow.get('2017-12-25') # 2017년 12월 25일을 arrow 형태로 가져온다.
<Arrow [2017-12-25T00:00:00+00:00]>
시간 단위까지 넣어주고 싶다면 날짜 뒤에 넘겨주면 된다.
date = arrow.get('2017-12-25 16:23') # 17년 12월 25일 오후 4시 23분을 넘겨준다.
date
<Arrow [2017-12-25T16:23:00+00:00]>
원하는 날짜가 arrow 형식으로 저장된 것을 확인할 수 있다.
문자열을 사용하지 않고도, 날짜를 지정할 수 있다.
date = arrow.get(2017, 12, 25) # 2017년 12월 25일을 arrow 형태로 가져온다.
date
<Arrow [2017-12-25T00:00:00+00:00]>
위와 같은 결과를 얻을 수 있다.
날짜가 어떻게 구성되어있는지 직접 지정할 수도 있다.
arrow.get('20171225', 'YYYYMMDD') # 연도 네 자리, 월 두자리, 일 두자리로 인식하게 지정
<Arrow [2017-12-25T00:00:00+00:00]>
.format()을 사용하여 날짜의 출력 형식을 지정할 수 있다.
예를 들어, 날짜를 '년/월' 형식으로 나타내고 싶다면 format에 'YY/MM'을 넘겨준다.
date.format('YY/MM')
'17/12'
Y 는 연도,
M 는 월,
D 는 일,
h 는 시,
m 은 분을 뜻한다.
date.format('YYYY.MM')
'2017.12'
M 4개를 사용하면 숫자 대신 영어로 월이 출력된다.
date.format('YYYY.MMMM')
'2017.December'
date.format('YY년 MM월 DD일')
'17년 12월 25일'
현재 시간이 아닌 특정 시간을 다른 곳의 시간대로 변환하고 싶다면 .to() 사용하면 된다.
예를 들어, 한국의 2017년 12월 25일 16시 43분이 미태평양에서 언제인지 알아보자.
date = arrow.get('2017-12-25 16:23') # 우선 2017/12/25 16시 43분을 arrow 형태로 받아옴.
date.to('US/Pacific') # 시간을 미태평양 시간으로 받는다.
<Arrow [2017-12-25T08:23:00-08:00]>
미 태평양은 한국이 16시 일 때, 오전 8시인 것을 알 수 있다.
.shift()이용해, 해당 날짜를 다른 날짜로 옮길 수도 있다.
three_weeks_later = date.shift(weeks=3) # date 를 3 주후의 시간으로 옮긴다.
<Arrow [2018-01-15T16:23:00+00:00]>
three_weeks_before = date.replace(weeks=-3) # date 를 3주 전의 시간으로 옮긴다.
<Arrow [2017-12-04T16:23:00+00:00]>
12월 25일의 3주 전인 12월 4일로 지정되었다.
주 단위 외에도 년, 일, 시간 단위 까지 이동을 할 수 있다.
date.replace(days=3) # date 를 3일 후의 시간으로 옮긴다.
<Arrow [2017-12-28T16:23:00+00:00]>
날짜가 28일로 수정되었다.
'days' 가 아니라 'day'를 옵션에 넘겨주면 해당 일로 지정할 수 있다.
date.replace(day=3) # date 의 날짜를 3일로 옮긴다.
<Arrow [2017-12-03T16:23:00+00:00]>
그럼 date 가 12월 3 일인 날짜로 변경되었다.
.weekday()를 통해 해당 날짜가 무슨 요일이였는지도 알 수 있다.
date.weekday() # date 변수의 날짜가 무슨 요일이였는지 알아보자.
0
date 는 12월 3일의 결과가 '0'으로 나온다.
'0' 이 월요일이고, '6' 은 일요이다.
arrow에서 - 사용해 날짜에서 날짜를 뺄 수 있다.
date1 = arrow.get('2017-11-12') # date1 을 11월 12일로 지정한다.
date2 = arrow.get('2017-12-01') # date2 를 12월 1일로 지정한다.
difference = date2 - date1 # date2 에서 date1 을 빼준다.
.days() 를 통해 며칠인지 확인한다.
difference.days # difference.days 로 총 며칠인지 확인한다.
19
'date2' 와 'date1'의 차이가 19일인 것을 알 수 있다.
Pillow 라는 라이브러리는 이미지 처리할 때 사용되는 유용한 라이브러리이다.
공식문서 - (https://pillow.readthedocs.io/)
from PIL import Image
import os
작업 폴더에 마음에 드는 사진을 넣어둔다.
image_path = 'images/gecko.jpg'
Pillow 의 Image를 통해 사진을 연다.
image = Image.open(image_path)
image
<PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=275x183 at 0x2429BD2DC18>
image.resize((128, 128)) # 픽셀 단위
<PIL.Image.Image image mode=RGB size=128x128 at 0x2429B1C1400>
resize = image.resize((128, 128))
resize.save('images/resized.jpg')
resize.rotate(45) # 반시계방향 각도
<PIL.Image.Image image mode=RGB size=128x128 at 0x2429BDF6FD0>
resize.transpose(Image.FLIP_TOP_BOTTOM)
<PIL.Image.Image image mode=RGB size=128x128 at 0x2429BDFE080>
resize.transpose(Image.FLIP_LEFT_RIGHT)
<PIL.Image.Image image mode=RGB size=128x128 at 0x1CD76DE1B00>
resize.transpose(Image.ROTATE_90)
<PIL.Image.Image image mode=RGB size=128x128 at 0x2429BD2DCC0>
Pillow 의 ImageFilter를 통해 사진에 필터링을 할 수 있다.
from PIL import ImageFilter
resize.filter(ImageFilter.BLUR)
<PIL.Image.Image image mode=RGB size=128x128 at 0x1CD76DF3EB8>
resize.filter(ImageFilter.EMBOSS)
<PIL.Image.Image image mode=RGB size=128x128 at 0x1CD76DF3FD0>
resize.filter(ImageFilter.CONTOUR)
<PIL.Image.Image image mode=RGB size=128x128 at 0x1CD76DFC0F0>
resize.filter(ImageFilter.DETAIL)
<PIL.Image.Image image mode=RGB size=128x128 at 0x1CD76DFC1D0>
resize.filter(ImageFilter.EDGE_ENHANCE)
<PIL.Image.Image image mode=RGB size=128x128 at 0x1CD76DFC358>
resize.filter(ImageFilter.SMOOTH)
<PIL.Image.Image image mode=RGB size=128x128 at 0x1CD76DFC198>
.convert()에 색상 표현 방식을 넘겨줘서 이미지를 원하는 색상표현으로 변경할 수 있다.
자주 사용되는 방식들은 다음과 같다
이미지에 .convert() 함수에 L, RGB, RGBA 등을 넘겨줘서 원하는 색상표현 방식으로 변환할 수 있다.
.convert()에 'L'을 넘겨줘서 8 비트 그레이스케일로 변환할 수 있다.
resize.convert('L') # ITU-R 601-2 luma transform
<PIL.Image.Image image mode=L size=128x128 at 0x1CD76DFC400>
행렬로 변환하는 것은 numpy.asarray를 사용한다.
import numpy
m = numpy.asarray(resize)
형태를 보면 가로 128, 세로 128인 행렬 3개가 겹쳐진 형태가 된다.
m.shape
(128, 128, 3)
흑백 이미지를 행렬로 바꿔보자.
bw = numpy.asarray(resize.convert('L'))
가로 128, 세로 128인 행렬 1개가 된다.
bw.shape
(128, 128)
사진에 텍스트를 한번 넣어보자.
from PIL import ImageDraw, ImageFont
우선 텍스트를 넣을 사진을 불러온다.
base = Image.open(os.path.join(os.getcwd(), 'images', images[1])).convert('RGBA')
# 흰색-투명으로 이미지와 같은 크기 이미지 하나 더 만들기
txt = Image.new('RGBA', base.size, (255,255,255,0))
d = ImageDraw.Draw(txt)
d.text((10,10), "Hello", fill=(255,255,255,128)) # 위치, 텍스트, 흰색-반투명
d.text((10,60), "World", fill=(255,255,255,255)) # 위치, 텍스트, 흰색-불투명
Image.alpha_composite(base, txt)
<PIL.Image.Image image mode=RGBA size=128x128 at 0x1CD76E039B0>
RGBA 형태의 그림은 .png 파일로 저장해야한다. .jpg 파일로 저장하면 오류가 발생한다.
3 개의 이미지 파일을 불러오자.
im1 = Image.open('images/gecko.jpg')
im2 = Image.open('images/gecko_resized.jpg')
im3 = Image.open('images/gecko_rotated.jpg')
image_list = [im2, im3]
im1 = im1.convert('RGB')
이미지의 사이즈를 조정하고 싶다면 im.resize(x 크기, y 크기) 로 지정하면 된다.
tiff 파일을 먼저 생성을 해야 pdf 가 생성이 된다.
im1.save('images.tiff', save_all=True, append_images=image_list)
im1.save('images.pdf', save_all=True, append_images=image_list)
후에 PyPDF2 를 사용해 사진을 다른 PDF 페이지 사이에 끼워넣을 수 있다.
코드를 짜다보면, 늘상 에러가 난다.
ZeroDivisionError Traceback (most recent call last)
<ipython-input-58-2b706ee9dd8e> in <module>()
----> 1 3 / 0
ZeroDivisionError: division by zero
예시의 에러같은 경우, 3 을 0 으로 나누려고 하니 ZeroDivisionError 가 발생했다.
이번 시간에는 에러가 발생할때, try 와 except 를 사용해, 예외사항을 처리하는 방법을 알아보자.
먼저, 평균을 구하는 함수를 만든다.
def mean(lst): # 숫자가 들어있는 리스트를 인수로 받는다.
합계 = sum(lst) # 썸으로 리스트의 합계를 구하고,
개수 = len(lst) # 렌으로 리스트의 개수를 구한다.
return 합계 / 개수 # 합계를 개수로 나누어 돌려준다.
mean([7, 3, 5])
5.0
mean 함수에 [7, 3, 5] 를 넘겨주면, 결과로 평균값 5가 나온다.
하지만 빈 리스트를 넘겨주면,
mean([])
Traceback (most recent call last)
<ipython-input-142-e9e2222d800d> in <module>()
----> 1 mean([])
<ipython-input-140-a06ba1720bc6> in mean(lst)
2 합계 = sum(lst) # 썸으로 합계를 구하고,
3 개수 = len(lst) # 렌으로 개수를 구한다.
----> 4 return 합계 / 개수 # 합계를 개수로 나누어 돌려준다.
ZeroDivisionError: division by zero
개수가 0이 되면서, ZeroDivisionError 가 발생한다.
에러가 발생해도 프로그램이 멈추지 않고 실행될 수 있도록 예외 처리를 할 수 있다.
예외 처리를 할 때에는 try, except 라는 구문을 사용한다.
만약 에러가 난다면, except 밑에 있는 코드가 실행 되고 에러가 발생하지 않는다면 그대로 실행된다.
try: # try 후에,
<에러가 날 수 있는 코드> # 들여쓰기를 하고 실행시킬 코드를 넣는다.
except 발생 에러: # except 하고 지정한 에러가 일어날 경우,
<에러가 발생하면 실행되는 코드> # 실행할 코드를 입력한다.
mean 을 0 으로 나눌때, ZeroDivisionError 가 발생하지 않도록 try, except 구문을 적어보자.
try:
mean([])
except ZeroDivisionError: # zero division error 가 발생할 경우
print('0 으로 나누려고 합니다') # '0 으로 나누려고 합니다'를 프린트
0 으로 나누려고 합니다
이번에는 에러가 발생하지 않고, 미리 지정해놓은 문구가 출력되었다.
이번에는 정상적인 숫자 리스트를 mean 함수에 넘겨준다.
try:
print(mean([3, 4]))
except ZeroDivisionError:
print('0 으로 나누려고 합니다')
3.5
문제없이, 평균값이 나왔다.
except 뒤에 에러를 지정하여 발생하는 에러에 대해서 예외사항으로 처리할 수 있다.
sum 함수는 문자열을 처리할 수 없기 때문에 TypeError 가 발생한다.
mean('hello') # mean 함수에 헬로라는 문자열을 넘겨준다.
Traceback (most recent call last)
<ipython-input-146-bfd617a58807> in <module>()
----> 1 mean('hello')
<ipython-input-140-a06ba1720bc6> in mean(lst)
1 def mean(lst): # 숫자가 들어있는 리스트를 인수로 받는다.
----> 2 합계 = sum(lst) # 썸으로 합계를 구하고,
3 개수 = len(lst) # 렌으로 개수를 구한다.
4 return 합계 / 개수 # 합계를 개수로 나누어 돌려준다.
TypeError: unsupported operand type(s) for +: 'int' and 'str'
문자열이 들어올 경우 숫자 리스트를 넣어달라는 문구를 프린트하도록 한다.
try:
print(mean('hello'))
except TypeError: # 만약 타입에러가 났다면,
print('숫자 리스트를 넣어주세요') # '숫자 리스트를 넣어주세요'를 프린트 한다.
숫자 리스트를 넣어주세요
try 뒤에 여러 except 문을 넣을 수 있다.
try:
print(mean([]))
except TypeError: # Type Error 일때,
print('숫자 리스트를 넣어주세요') # 프린트할 메세지와,
except ZeroDivisionError: # zero division error 일때
print('0 으로 나누려고 합니다') # 프린트할 메세지를 다르게 지정한다.
0 으로 나누려고 합니다
이렇게 두 종류의 에러에도 대응할 수 있는, try except 문이 생성되었다.
except 를 쓸때, 에러를 지정하지 않을 수 있다.
에러를 지정하지 않으면, 어떠한 에러가 발생해도 같은 처리를 한다.
try:
print(mean([]))
except:
print('문제가 생겼습니다')
문제가 생겼습니다
ZeroDivisionError 를 자세히 살펴보면,
3 / 0
ZeroDivisionError Traceback (most recent call last)
<ipython-input-2-2b706ee9dd8e> in <module>()
----> 1 3 / 0
ZeroDivisionError: division by zero
에러 이름 뒤에 division by zero 라는 설명이 나온다.
except 에서 이 설명이 출력되도록 지정할 수 있다.
try:
mean([])
except ZeroDivisionError as e:
print(e)
division by zero
ZeroDivisionError 가 발생할 때 나오는 문구인, division by zero 가 프린트 된다.
예외 처리에서, else 구문도 사용할 수 있다.
try / except문에서 에러가 발생하지 않을시, 실행될 블록을 지정할 수 있다.
try:
<에러가 날 수 있는 코드>
except 발생 에러:
<에러가 발생하면 실행되는 코드>
else: # try 뒤에 에러가 발생하지 않으면 바로 else 뒤의 코드가 실행
<예외 상황이 발생 안하면 실행되는 코드>
예제를 통해, 자세히 알아보자.
try 와 except 구문 뒤에, else 절을 추가한다.
try:
number = mean([3, 4])
except: # 에러가 발생하면,
print('에러입니다') # "에러입니다" 를 출력하고
else: # 에러가 발생하지 않으면,
print(number * 2) # 변수 number 에 2를 곱해 출력한다.
7
3과 4로 된 리스트를 mean 함수에 넘겨주면, 에러가 발생하지 않으므로 else 절로 넘어가서 print(number*2) 코드가 실행되었다.
특정 에러가 발생했을 때 알려줄 메세지를 raise 로 쉽게 바꿀 수 있다.
raise 뒤에 사용하고 싶은 에러와 출력할 문장을 넣어주면 메시지가 바뀐다.
raise IndexError('하하 그런 것 없어요')
IndexError Traceback (most recent call last)
<ipython-input-8-766dc464d4a8> in <module>()
----> 1 raise IndexError('하하 그런 것 없어요')
IndexError: 하하 그런 것 없어요
예시로 인수가 음수일 경우, ValueError 와 함께 '음수입니다' 라는 메시지를 띄우는 함수 fac 를 만들어보자.
def fac(n):
if n < 0:
raise ValueError('음수입니다')
양수인 숫자 10을 넣어주면 문제없이 코드가 실행된다.
fac(10)
하지만 음수를 넣어주면 ValueError 와 미리 지정한 에러 메시지를 띄운다.
fac(-1)
ValueError Traceback (most recent call last)
<ipython-input-11-d65c80c453f4> in <module>()
----> 1 fac(-1)
<ipython-input-9-12a49895dd4e> in fac(n)
1 def fac(n):
2 if n < 0:
----> 3 raise ValueError('음수? 음수? 뤼얼리?')
ValueError: 음수입니다
이번에는 python 에서 자주 발생하는 에러의 종류에 대해 알아보자.
python 에는 많은 에러가 있지만, 이 강의에서는 몇 가지만 보도록 한다.
SyntaxError, NameError, TypeError, ValueError, IndexError ...
SyntaxError 는 파이썬 문법에 어긋나게 코드를 작성한 경우 발생한다.
코드를 문법에 맞게 고치면 해결된다.
몇 가지 SyntaxError 상황을 보자.
1 +/ 1
File "<ipython-input-159-23a9bc70ce7f>", line 1
1 +/ 1
^
SyntaxError: invalid syntax
+/ 라는 연산기호가 없기 때문에 SyntaxError 가 발생한다.
1 + 1
1 / 1
1.0
위와 같이 수정하면 문제가 해결된다.
'안녕하세요
File "<ipython-input-160-104019af679f>", line 1
'안녕하세요
^
SyntaxError: EOL while scanning string literal
따옴표가 짝이 맞지 않아, SyntaxError 가 발생한다.
'안녕하세요'
안녕하세요
안녕하세요 뒤에 따옴표를 붙이면 문제가 해결된다.
(1+1
File "<ipython-input-161-095bac307b77>", line 1
(1+1
^
SyntaxError: unexpected EOF while parsing
(1 + 1)
2
위와 같은 경우는 닫는 괄호를 붙여주면 해결된다.
NameError 는 정의되지 않은 변수나 함수 등의 이름을 실행시킬 때 발생한다.
print(abc)
Traceback (most recent call last)
<ipython-input-164-c5a4f3535135> in <module>()
----> 1 print(abc)
NameError: name 'abc' is not defined
abc 라는 변수를 선언하지 않았기 때문에 NameError 가 발생한다.
abc = 3
print(abc)
3
abc 라는 변수를 3으로 지정하면 문제가 해결된다.
SyntaxError 나 NameError 는 주로 오타 문제로 비교적으로 쉽게 해결이 가능하다.
다음 에러들은, 다루고 있는 데이터와 관련해 발생하는 에러들이다.
TypeError 는 적절하지 않은 자료형 때문에 발생한다.
sum 함수는 합계를 구하는 함수인데, sum 함수에 문자열 리스트를 넘겨주도록 하자.
sum(['one', 'two', 'three'])
Traceback (most recent call last)
<ipython-input-173-f98a3e650ff1> in <module>()
----> 1 sum(['one', 'two', 'three'])
TypeError: unsupported operand type(s) for +: 'int' and 'str'
문자열은 합할 수가 없기 때문에 TypeError 가 발생한다.
sum([1, 2, 3])
6
반면, 숫자를 넘겨주면 TypeError 가 발생하지 않고 해결된다.
ValueError 는 자료형은 맞지만 값이 틀린 경우이다.
예를 들어, 인티저 함수는 문자열을 받을 수 있다.
하지만 숫자가 아닌, 알파벳 'a' 를 인티저로 바꿀 수는 없다.
int('a')
Traceback (most recent call last)
<ipython-input-131-b3c3f4515dd4> in <module>()
----> 1 int('a')
ValueError: invalid literal for int() with base 10: 'a'
따라서, ValueError 가 발생한다.
int 에는 값이 숫자인 문자열을 넘겨줘야 에러가 발생하지 않는다.
int('3')
3
IndexError 는 선택한 인덱스가 주어진 범위를 벗어나면 발생한다.
alphabet = ['a', 'b']
alphabet[5] # alphabet 이라는 리스트에서 6번째 값을 선택한다.
Traceback (most recent call last)
<ipython-input-139-b6a934feab86> in <module>()
----> 1 alphabet[5]
IndexError: list index out of range
6 번째 값은 alphabet 리스트의 범위를 벗어났기 때문에 IndexError가 발생한다.
주어진 범위를 넘어가지 않는 인덱스를 선택해야 에러가 발생하지 않는다.
alphabet[1]
'b'
사전에 존재하지 않는 key 를 불러올때 KeyError 가 발생한다.
fruit = {'사과': '100원', '바나나': '200원', '포도': '150원'}
fruit 이라는 사전에 존재하지 않는 key 인 망고를 불러오면 KeyError 가 발생한다.
fruit['망고']
Traceback (most recent call last)
<ipython-input-170-b397f48a5f8c> in <module>()
----> 1 fruit['망고']
KeyError: '망고'
KeyError 를 피하기 위해 망고를 부를 때 get 을 사용하면 된다.
fruit.get('망고', '300원') # 만약, 망고가 사전에 없다면 삼백원이라는 값을 돌려주도록 한다.
'300원'
KeyError 가 발생하지 않고 해결된 것을 확인할 수 있다.
이외에도 다양한 에러가 발생할 수 있지만, 이 강의에서는 자주 일어나는 에러 몇 가지만 다루었다.
함수(function)는 특정한 작업을 하는 코드 조각이다. Python에서 함수를 부를 때는 항상 뒤에 괄호(())를 붙여준다.
함수에는 어떤 값들을 넘겨 줄 수 있는데 이를 인자(argument)라고 한다. 인자는 괄호 안에 순서대로 쓴다. 예를 들어, 파이썬 내장 함수인 round 에 인자로 3.5를 넘겨주면,
round(3.5)
4
반올림 되어서 4가 출력된다.
함수를 정의할 때는 def 라는 키워드를 사용한다. def 는 정의를 뜻하는 definition 의 약자이다.
'안녕' 이라는 문자열을 반환하는 hello 라는 함수를 만들어보자.
def hello():
return '안녕'
함수를 실행해보면 '안녕'이라는 결과가 반환된다.
hello()
안녕
함수의 실행 결과는 변수에 저장할 수 있다.
a = hello()
a
안녕
이번에는 이름을 넘겨 받아 인사에 포함시키는 hello_name라는 함수를 만들어보자.
def hello_name(name):
return '안녕 ' + name
이제 hello_name을 호출 할 때 괄호 안에 값을 인자로 넘겨주면, 그 이름은 함수 안에서 name으로 사용된다.
hello_name('헤이즐')
안녕 헤이즐
만약, 함수에 인자를 넘겨주지 않으면
hello_name()
TypeError Traceback (most recent call last)
<ipython-input-9-ec6643fba34e> in <module>()
----> 1 hello_name()
TypeError: hello_name() missing 1 required positional argument: 'name'
name 이라는 인자를 받지 못했다고 TypeError 를 낸다.
인자를 여러 개 넣을 수 있다.
def hello_punc(name, punc):
return '안녕 ' + name + punc
hello_punc('헤이즐', '!')
안녕 헤이즐!
함수의 인자가 여러 가지일 때 매번 모든 인자를 넣어주기 번거로울 수 있다. 이때 인자의 기본값을 지정할 수 있다. 아래의 예에서는 punc의 기본값을 느낌표로 지정한다.
def hello_punc(name, punc='!'):
return '안녕 ' + name + punc
이제 hello_punc를 호출 할 때 punc를 넘겨주지 않으면 기본값인 느낌표를 사용한다.
hello_punc('헤이즐')
안녕 헤이즐!
인자를 넘겨주면, 넘겨준 인자를 사용한다.
hello_punc('헤이즐', '?')
안녕 헤이즐?
아래 함수는 '안녕'이라고 출력하고 아무 것도 반환하지 않는다.
def print_hello():
print('안녕')
x = print_hello()
안녕
x에는 아무 것도 들어있지 않다.
x
x에는 없음을 뜻하는 None이 들어있다.
x == None
True
여러 개의 값을 반환할 때는 콤마로 구분한다. 다음 함수는 반지름을 인자로 넘겨 받아 원의 둘레와 넓이를 계산하여 반환한다.
import math
def circle(r):
둘레 = 2 * math.pi * r
면적 = math.pi * r ** 2
return 둘레, 면적
circle 함수를 호출한다.
circle(1)
6.283185307179586, 3.141592653589793
두 개의 값이 튜플로 반환된다. 만약 변수에 할당하고 싶으면 아래와 같은 형태를 사용할 수 있다.
c, a = circle(1)
함수 밖에서 정의된 변수는 함수 안쪽에 사용할 수 있다. 하지만 함수 안쪽에서 정의된 함수는 함수 바깥쪽에서 사용될 수 없다.
a = 1
add 함수를 생성한다.
def add(x):
b = x + 2
return x + a
add(3)
4
결과로 4 이라는 값이 출력된다. 함수 안에서는 바깥쪽에서 정의된 a를 부를 수 있기 때문이다.
함수 안에서 정의된 b를 바깥쪽에서 불러보면
b
NameError: name 'b' is not defined
NameError 가 발생한다.
Python 파일은 .py 확장자를 붙인다. 하나의 파이썬 파일은 "모듈"이라고 부른다. 다음과 같은 파일을 작성해서 magic.py로 저장하자.
var = 1
def add_one(x):
return x + 1
모듈은 import 문으로 불러서 사용할 수 있다.
import magic
import한 모듈 이름 뒤에 .을 붙이면 모듈의 변수나 함수에 접근할 수 있다.
magic.add_one(1)
2
from과 import를 사용하면 모듈의 변수나 함수를 직접 불러올 수 있다.
from magic import add_one
add_one(1)
2
명령창에서 모듈을 직접 실행할 수도 있다.
python magic.py
.py 라는 확장자로 끝나는 파일은 파이썬 에디터로 열고 작업할 수 있다.
다양한 파이썬 에디터가 있지만, 이번 시간에는 Atom 과 PyCharm 에 대한 소개를 하겠다.
Atom 은 간단하게, 파이썬 파일을 작성하거나 편집할 수 있는 에디터이다.
Atom 웹사이트에 접속한다 (https://atom.io/).
다운로드 버튼을 눌러 설치파일을 받아 실행시킨다.
Atom 을 실행시킨다.
Atom 이 실행된다.
.py 파일을 불러오고 싶을때는, File - Open 을 들어가 파일을 불러온다.
.py 파일이 불려왔다.
Atom 에서 작성 및 편집 작업을 거칠 수 있다.
Atom 에는 많은 기능이 제한되어 있다.
복잡한 파이썬 작업을 해야할 경우, PyCharm 과 같이 많은 기능을 제공하는 에디터가 필요하다.
PyCharm 다운로드 사이트에 접속한다. (https://www.jetbrains.com/pycharm/download)
기능이 더욱 많은 전문가 버전을 사용할 수 있지만, 일단 무료 버전인 Community 버전을 설치한다.
PyCharm 을 실행한다.
File - Open 으로 .py 파일을 연다.
.py 파일이 불려왔다.
PyCharm 에서는 project 단위로 작업을 진행하는 것이 수월하다.
File - New Project 로 새로운 프로젝트를 만든다.
Location 에서 프로젝트 명을 변경하고,
Interpreter 에서 Create Conda Env 를 클릭한다. Interpreter 는 사용할 파이썬을 의미한다.
새로운 Conda Environment 의 이름을 만든 후, 사용할 Python version 과 Location 을 확인한다.
OK 를 누른 후 Create 를 누르면, 환경이 만들어진다.
다시 .py 파일을 열어보자.
.py 의 실행 결과를 확인하고 싶다면, 우측 상단의 Run 버튼 을 클릭한다. 아래 창에서 파일의 실행 결과를 볼 수 있다.
PyCharm 에서 .ipynb 파일을 열수도 있다.
파일을 실행시키기고 싶다면, 먼저 주피터 노트북을 띄우듯 코맨드 창에서 주피터 노트북 커널을 띄워, token 이 있는 URL 을 복사한다.
다음, PyCharm 에서 Run 버튼 을 누른다.
주소 입력창에 복사한 URL을 붙여넣는다.
이제, 주피터 노트북과 같이 사용할 수 있다.
pyinstaller 를 통해 파이썬 파일을 실행 파일인 .exe 형태로 저장할 수 있다.
pip install pyinstaller 를 통해서 설치할 수 있다.
pip install pyinstaller
.exe 파일로 변경할 파이썬 파일을 우선 생성하자.
아주 간단한 GUI (Graphical User Interface) 파일을 생성하자.
tkinter 라는 패키지를 불러온다.
import tkinter as tk
tkinter 에서 Tk를 이용하여 새로운 GUI 를 생성할 수 있다.
root = tk.Tk() # 메인 작업이 저장된다
root.title('Testing !') # GUI 제목을 설정한다
root.mainloop() # 저장한 파일을 실행한다
위의 코드를 jupyter notebook 에서 실행시키면
제목만 Testing ! 으로 설정된 별도의 작은 GUI 창이 뜬다.
해당 파일을 .exe 파일로 생성하여 클릭만으로 GUI 창을 실행시켜보자.
위에서 사용된 코드를 메모장에 붙여넣은 후에 gui.py 파일로 저장한다.
파이썬 파일로 저장된 것을 확인할 수 있다.
gui.py 를 클릭하여도 GUI 창이 실행된다.
이 파일을 .exe 파일로 변환하자.
해당 파이썬 파일이 있는 위치에서 command 창을 띄운다.
주소창에 cmd 를 입력하거나 Shift 를 누른채로 빈 곳에 우클릭하여 명령창을 실행한다.
실행한 command 창에 pyinstaller와 파일이름을 같이 입력한다.
pyinstaller gui.py
여러개의 파일이 생성된 것을 확인할 수 있다.
dist라는 파일 안에 gui 라는 폴더를 클릭한다.
gui.exe 라는 실행 파일이 생성된 것을 확인할 수 있다.
해당 파일을 실행시키면,
이전과 같이 GUI 창이 실행된 것을 확인할 수 있다.
Pandas는 데이터를 쉽게 가공할 수 있도록 돕는다. SQL, 엑셀 파일을 표로 읽어올 수 있으며, 데이터 분석하는데 효과적인 자료구조와 기능들을 제공한다.
Matplotlib는 자료를 차트나 플롯으로 시각화하는 도구이다. 많은 파이썬 시각화 툴의 기본이 되며, 제공하는 기능이 많지만 초보자에게 사용 방법이 어렵다고 느껴질 수 있다.
Seaborn은 matplotlib를 바탕으로 만든 시각화 툴이다. Pandas의 데이터 프레임을 사용해 더욱 쉽게 그래프를 그릴 수 있도록 돕는다.
Statsmodels는 python에서 다양한 통계 분석을 할 수 있도록 기능을 제공한다.
Scikit-learn은 python에서 머신러닝을 쉽게 적용할 수 있도록 여러 기능을 제공한다.
Pandas로 자료를 가져올 수 있으며 분석에 적합하도록 가공을 할 수 있다.
import pandas as pd # pandas 를 pd 로 불러온다.
"Guerry" 데이터 셋을 사용해보자.
"Guerry" 데이터 셋은 A.-M. Guerry의 "Essay on the Moral Statistics of France" 연구에서 공개된 자료로, 1830년도 프랑스의 사회인구학적 데이터를 담고 있다.
다음은 이 데이터 셋의 몇 가지 지표 예시이다.
url = 'https://vincentarelbundock.github.io/Rdatasets/csv/HistData/Guerry.csv'
original_df = pd.read_csv(url)
original_df = pd.read_csv(url, index_col=0)
# `index_col=0`은 첫 열을 index로 지정
head()로 데이터프레임의 첫 5행을 확인할 수 있다. .head()에 특정 숫자를 넘겨주면 해당 숫자 만큼의 행을 출력한다.
original_df.head()
| dept | Region | Department | Crime_pers | Crime_prop | Literacy | Donations | Infants | Suicides | MainCity | ... | Crime_parents | Infanticide | Donation_clergy | Lottery | Desertion | Instruction | Prostitutes | Distance | Area | Pop1831 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 1 | E | Ain | 28870 | 15890 | 37 | 5098 | 33120 | 35039 | 2:Med | ... | 71 | 60 | 69 | 41 | 55 | 46 | 13 | 218.372 | 5762 | 346.03 |
| 2 | 2 | N | Aisne | 26226 | 5521 | 51 | 8901 | 14572 | 12831 | 2:Med | ... | 4 | 82 | 36 | 38 | 82 | 24 | 327 | 65.945 | 7369 | 513.00 |
| 3 | 3 | C | Allier | 26747 | 7925 | 13 | 10973 | 17044 | 114121 | 2:Med | ... | 46 | 42 | 76 | 66 | 16 | 85 | 34 | 161.927 | 7340 | 298.26 |
| 4 | 4 | E | Basses-Alpes | 12935 | 7289 | 46 | 2733 | 23018 | 14238 | 1:Sm | ... | 70 | 12 | 37 | 80 | 32 | 29 | 2 | 351.399 | 6925 | 155.90 |
| 5 | 5 | E | Hautes-Alpes | 17488 | 8174 | 69 | 6962 | 23076 | 16171 | 1:Sm | ... | 22 | 23 | 64 | 79 | 35 | 7 | 1 | 320.280 | 5549 | 129.10 |
5 rows × 23 columns
자료의 대략적인 모양을 확인할 수 있다.
.tail()은 .head()와 반대로 자료의 끝부분을 확인해볼 수 있다.
original_df.tail()
| dept | Region | Department | Crime_pers | Crime_prop | Literacy | Donations | Infants | Suicides | MainCity | ... | Crime_parents | Infanticide | Donation_clergy | Lottery | Desertion | Instruction | Prostitutes | Distance | Area | Pop1831 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 82 | 86 | W | Vienne | 15010 | 4710 | 25 | 8922 | 35224 | 21851 | 2:Med | ... | 20 | 1 | 44 | 40 | 38 | 65 | 18 | 170.523 | 6990 | 282.73 |
| 83 | 87 | C | Haute-Vienne | 16256 | 6402 | 13 | 13817 | 19940 | 33497 | 2:Med | ... | 68 | 6 | 78 | 55 | 11 | 84 | 7 | 198.874 | 5520 | 285.13 |
| 84 | 88 | E | Vosges | 18835 | 9044 | 62 | 4040 | 14978 | 33029 | 2:Med | ... | 58 | 34 | 5 | 14 | 85 | 11 | 43 | 174.477 | 5874 | 397.99 |
| 85 | 89 | C | Yonne | 18006 | 6516 | 47 | 4276 | 16616 | 12789 | 2:Med | ... | 32 | 22 | 35 | 51 | 66 | 27 | 272 | 81.797 | 7427 | 352.49 |
| 86 | 200 | NaN | Corse | 2199 | 4589 | 49 | 37015 | 24743 | 37016 | 2:Med | ... | 81 | 2 | 84 | 83 | 9 | 25 | 1 | 539.213 | 8680 | 195.41 |
5 rows × 23 columns
86번째 행의 "Region" 열에 NaN 이 보인다. NaN은 'Not-a-Number'의 줄임말로 값이 없다는 뜻이다. 이를 .dropna() 메소드로 제거해보자.
inplace=True 옵션을 넘겨주면 실행결과를 현재 변수에 다시 저장한다. inplace=False 옵션을 넘겨주면 실행결과가 출력만 되고 실제 데이터 프레임에는 변화가 없으므로 새로운 변수에 저장해줘야한다.
original_df.dropna(inplace=True)
다시 확인해보니 NaN 이 들어있던 행은 제거되었다.
original_df.tail()
| dept | Region | Department | Crime_pers | Crime_prop | Literacy | Donations | Infants | Suicides | MainCity | ... | Crime_parents | Infanticide | Donation_clergy | Lottery | Desertion | Instruction | Prostitutes | Distance | Area | Pop1831 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 81 | 85 | W | Vendee | 20827 | 7566 | 28 | 14035 | 62486 | 67963 | 1:Sm | ... | 50 | 44 | 30 | 68 | 79 | 59 | 4 | 212.459 | 6720 | 330.36 |
| 82 | 86 | W | Vienne | 15010 | 4710 | 25 | 8922 | 35224 | 21851 | 2:Med | ... | 20 | 1 | 44 | 40 | 38 | 65 | 18 | 170.523 | 6990 | 282.73 |
| 83 | 87 | C | Haute-Vienne | 16256 | 6402 | 13 | 13817 | 19940 | 33497 | 2:Med | ... | 68 | 6 | 78 | 55 | 11 | 84 | 7 | 198.874 | 5520 | 285.13 |
| 84 | 88 | E | Vosges | 18835 | 9044 | 62 | 4040 | 14978 | 33029 | 2:Med | ... | 58 | 34 | 5 | 14 | 85 | 11 | 43 | 174.477 | 5874 | 397.99 |
| 85 | 89 | C | Yonne | 18006 | 6516 | 47 | 4276 | 16616 | 12789 | 2:Med | ... | 32 | 22 | 35 | 51 | 66 | 27 | 272 | 81.797 | 7427 | 352.49 |
5 rows × 23 columns
이 중 재산범죄, 문해율, 재산순위, 수도까지의 거리에 대한 자료만 선택해서 다른 데이터 프레임으로 저장하자.
df = original_df[['Crime_prop', 'Literacy', 'Wealth', 'Distance']]
df.head()
| Crime_prop | Literacy | Wealth | Distance | |
|---|---|---|---|---|
| 1 | 15890 | 37 | 73 | 218.372 |
| 2 | 5521 | 51 | 22 | 65.945 |
| 3 | 7925 | 13 | 61 | 161.927 |
| 4 | 7289 | 46 | 76 | 351.399 |
| 5 | 8174 | 69 | 83 | 320.280 |
새 데이터 프레임이 만들어졌다.
전처리한 자료에 회귀분석을 적용해 변수별 회귀계수를 구할 수 있다.
import statsmodels.api as sm
import statsmodels.formula.api as smf
문해율, 재산순위, 수도까지의 거리가 재산범죄에 미치는 영향을 살펴보자.
예시로 Ordinary Least Squares(OLS) 방식을 사용해보자.
res = smf.ols(formula='Crime_prop ~ Literacy + Wealth + Distance', data=df).fit()
res
<statsmodels.regression.linear_model.RegressionResultsWrapper at 0x1f9254eddd8>
분석에 대한 요약을 한눈에 볼 수 있다.
print(res.summary())
각 변수에 대한 재산범죄의 관계를 그래프로 그려보자.
%matplotlib inline
import seaborn as sns
import matplotlib.pyplot as plt
plt.subplot()은 subplot을 생성한다. plt.subplot(2, 3, 3)은 2 x 3 그래프에서 3번째에 있는 그래프를 뜻한다.
.regplot()은 데이터와 선형 회귀 모형을 그래프로 나타낸다.
plt.subplot(1, 3, 1) # 1 x 3 그래프 배열에서 첫 번째 그래프
sns.regplot('Literacy', 'Crime_prop', df, color='red')
plt.subplot(1, 3, 2) # 1 x 3 그래프 배열에서 두 번째 그래프
sns.regplot('Wealth', 'Crime_prop', df, color='blue')
plt.subplot(1, 3, 3) # 1 x 3 그래프 배열에서 세 번째 그래프
sns.regplot('Distance', 'Crime_prop', df, color='green')
<matplotlib.axes._subplots.AxesSubplot at 0x1f927a68cc0>
<matplotlib.figure.Figure at 0x1f9279253c8>
문해율은 높을수록 재산범죄당 인구가 줄어들며, 재산순위가 높고 수도까지의 거리가 멀어질수록 재산범죄당 인구가 늘어난다.
데이터 프레임에서 바로 .plot()을 실행하여 그래프를 그릴 수 있다.
df.plot()
<matplotlib.axes._subplots.AxesSubplot at 0x1eddc479048>
<matplotlib.figure.Figure at 0x1eddc22c5c0>
특정 그래프를 그리고 싶다면 kind= 옵션에 원하는 그래프 타입을 넘겨준다.
예를 들어 산점도를 그릴 경우, kind='scatter'라고 지정한다.
이때 x와 y 축에 해당하는 컬럼의 이름을 넘겨줘야 한다.
df.plot('Literacy', 'Crime_prop', kind='scatter',color='red')
df.plot('Wealth', 'Crime_prop', kind='scatter', color='blue')
<matplotlib.axes._subplots.AxesSubplot at 0x1ede072f4e0>
<matplotlib.figure.Figure at 0x1eddf31ef28>
<matplotlib.figure.Figure at 0x1ede080ae10>
이번에는 자료를 학습시켜 재산범죄를 예측해보자.
from sklearn.linear_model import LinearRegression
먼저 선형 회귀 모형을 불러온다.
model = LinearRegression()
변수 x에 학습 자료를 지정하고, 변수 y에 정답 자료를 지정한다.
x = df[['Literacy', 'Wealth', 'Distance']] # 학습 자료
y = df['Crime_prop'] # 정답 자료
.fit()을 통하여 학습자료와 정답자료를 학습시킨다.
model.fit(x, y)
LinearRegression(copy_X=True, fit_intercept=True, n_jobs=1, normalize=False)
동네A의 재산범죄를 예측해보자.
모형에 .predict() 메소드 실행하여 예측할 수 있다.
literacy = 80
wealth = 1
distance = 20
regionA = [literacy, wealth, distance]
crimeA = model.predict([regionA])
crimeA
array([ 3880.68993851])
3천명당 1건의 재산범죄를 예측할 수 있다.
동네B의 재산범죄를 예측해보자.
literacy = 10
wealth = 70
distance = 500
regionB = [literacy, wealth, distance]
crimeB = model.predict([regionB])
crimeB
array([ 10964.81313077])
1만명당 1건의 재산범죄를 예측할 수 있다.
두 예시의 중간 수준의 동네C의 재산범죄를 예측해보자.
literacy = 50
wealth = 30
distance = 300
regionC = [literacy, wealth, distance]
crimeC = model.predict([regionC])
crimeC
array([ 7073.04508028])
6천명당 1건의 재산범죄를 예측할 수 있다.
다양한 형태의 자료를 판다스로 불러오고 내보내보자.
import pandas as pd
csv 파일은 Comma-Separated Values 를 의미한다. 즉, (, ;, \t) 등으로 구분이 되어있는 자료를 의미한다.
csv 자료를 불러올때는 .read_csv 를 사용한다.
bank = pd.read_csv('bank.csv') # bank 로 저장
자료가 불려왔는지 확인해보자.
bank.head()
| age;"job";"marital";"education";"default";"balance";"housing";"loan";"contact";"day";"month";"duration";"campaign";"pdays";"previous";"poutcome";"y" | |
|---|---|
| 0 | 30;"unemployed";"married";"primary";"no";1787;... |
| 1 | 33;"services";"married";"secondary";"no";4789;... |
| 2 | 35;"management";"single";"tertiary";"no";1350;... |
| 3 | 30;"management";"married";"tertiary";"no";1476... |
| 4 | 59;"blue-collar";"married";"secondary";"no";0;... |
자료가 ; 로 구분이 되어있는데, 구분이 이뤄지지 않았다.
sep 옵션에 ; 를 넘겨주면 된다. sep 는 separator의 약자로 무엇으로 구분할지 지정할 수 있다.
bank = pd.read_csv('bank.csv', sep=';')
bank.head()
| age | job | marital | education | default | balance | housing | loan | contact | day | month | duration | campaign | pdays | previous | poutcome | y | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 30 | unemployed | married | primary | no | 1787 | no | no | cellular | 19 | oct | 79 | 1 | -1 | 0 | unknown | no |
| 1 | 33 | services | married | secondary | no | 4789 | yes | yes | cellular | 11 | may | 220 | 1 | 339 | 4 | failure | no |
| 2 | 35 | management | single | tertiary | no | 1350 | yes | no | cellular | 16 | apr | 185 | 1 | 330 | 1 | failure | no |
| 3 | 30 | management | married | tertiary | no | 1476 | yes | yes | unknown | 3 | jun | 199 | 4 | -1 | 0 | unknown | no |
| 4 | 59 | blue-collar | married | secondary | no | 0 | yes | no | unknown | 5 | may | 226 | 1 | -1 | 0 | unknown | no |
csv 파일이 바르게 불려왔다.
http://archive.ics.uci.edu/ml/datasets/Adult 에서 데이터를 다운 받는다. 이번 파일은 사람들의 성별, 나이, 노동시간 등의 정보를 담고 있다.
adult = pd.read_csv('adult.data')
adult.head()
| 39 | State-gov | 77516 | Bachelors | 13 | Never-married | Adm-clerical | Not-in-family | White | Male | 2174 | 0 | 40 | United-States | <=50K | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 50 | Self-emp-not-inc | 83311 | Bachelors | 13 | Married-civ-spouse | Exec-managerial | Husband | White | Male | 0 | 0 | 13 | United-States | <=50K |
| 1 | 38 | Private | 215646 | HS-grad | 9 | Divorced | Handlers-cleaners | Not-in-family | White | Male | 0 | 0 | 40 | United-States | <=50K |
| 2 | 53 | Private | 234721 | 11th | 7 | Married-civ-spouse | Handlers-cleaners | Husband | Black | Male | 0 | 0 | 40 | United-States | <=50K |
| 3 | 28 | Private | 338409 | Bachelors | 13 | Married-civ-spouse | Prof-specialty | Wife | Black | Female | 0 | 0 | 40 | Cuba | <=50K |
| 4 | 37 | Private | 284582 | Masters | 14 | Married-civ-spouse | Exec-managerial | Wife | White | Female | 0 | 0 | 40 | United-States | <=50K |
이 자료의 특징으로는, 열의 이름이 없어, 첫 행이 열의 이름으로 불려왔다는 점이다.
이런 상황을 예방하고 싶다면, header 라는 옵션을 None으로 지정하면 된다.
adult = pd.read_csv('adult.data', header=None)
adult.head()
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 39 | State-gov | 77516 | Bachelors | 13 | Never-married | Adm-clerical | Not-in-family | White | Male | 2174 | 0 | 40 | United-States | <=50K |
| 1 | 50 | Self-emp-not-inc | 83311 | Bachelors | 13 | Married-civ-spouse | Exec-managerial | Husband | White | Male | 0 | 0 | 13 | United-States | <=50K |
| 2 | 38 | Private | 215646 | HS-grad | 9 | Divorced | Handlers-cleaners | Not-in-family | White | Male | 0 | 0 | 40 | United-States | <=50K |
| 3 | 53 | Private | 234721 | 11th | 7 | Married-civ-spouse | Handlers-cleaners | Husband | Black | Male | 0 | 0 | 40 | United-States | <=50K |
| 4 | 28 | Private | 338409 | Bachelors | 13 | Married-civ-spouse | Prof-specialty | Wife | Black | Female | 0 | 0 | 40 | Cuba | <=50K |
이번에는 엑셀 파일을 불러와보자.
http://archive.ics.uci.edu/ml/datasets/Forest+Fires 에서 데이터를 다운받아 엑셀 파일로 먼저 변환을 한다. 엑셀파일은 .read_excel로 불러올 수 있다.
forest = pd.read_excel('forestfires.xlsx')
forest.head()
| X | Y | month | day | FFMC | DMC | DC | ISI | temp | RH | wind | rain | area | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 7 | 5 | mar | fri | 86.2 | 26.2 | 94.3 | 5.1 | 8.2 | 51 | 6.7 | 0.0 | 0.0 |
| 1 | 7 | 4 | oct | tue | 90.6 | 35.4 | 669.1 | 6.7 | 18.0 | 33 | 0.9 | 0.0 | 0.0 |
| 2 | 7 | 4 | oct | sat | 90.6 | 43.7 | 686.9 | 6.7 | 14.6 | 33 | 1.3 | 0.0 | 0.0 |
| 3 | 8 | 6 | mar | fri | 91.7 | 33.3 | 77.5 | 9.0 | 8.3 | 97 | 4.0 | 0.2 | 0.0 |
| 4 | 8 | 6 | mar | sun | 89.3 | 51.3 | 102.2 | 9.6 | 11.4 | 99 | 1.8 | 0.0 | 0.0 |
.to_csv로 데이터 프레임을 파일로 저장할 수 있다.
forest.to_csv('forest.csv')
해당 파일이 저장되었는지 확인해보자.
import os
os.path.exists('forest.csv') # 파일이 존재하는지 확인
True
이번에는 adult를 csv 로 저장해보자.
adult.to_csv('adult2.csv')
파일을 확인해보니, 행 이름은 전에 연습삼아 지정해놓은 대로 저장되었으며, 열 이름은 숫자로 되어있다.
header옵션과 index 를 지정하면, 이를 해결할 수 있다.
adult.to_csv('adult2.csv', header=0, index=0)
다시 확인해보니, 이번에는 행과 열 이름이 사라졌다.
데이터 프레임을 다루는 구체적인 방법을 살펴보자.
import pandas as pd
예제 데이터를 불러와보자.
예제 데이터는 포르투갈에서 발생한 산불과 관련된 데이터이다.
df = pd.read_csv('forest.csv')
df.head()
| Unnamed: 0 | X | Y | month | day | FFMC | DMC | DC | ISI | temp | RH | wind | rain | area | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 7 | 5 | mar | fri | 86.2 | 26.2 | 94.3 | 5.1 | 8.2 | 51 | 6.7 | 0.0 | 0.0 |
| 1 | 1 | 7 | 4 | oct | tue | 90.6 | 35.4 | 669.1 | 6.7 | 18.0 | 33 | 0.9 | 0.0 | 0.0 |
| 2 | 2 | 7 | 4 | oct | sat | 90.6 | 43.7 | 686.9 | 6.7 | 14.6 | 33 | 1.3 | 0.0 | 0.0 |
| 3 | 3 | 8 | 6 | mar | fri | 91.7 | 33.3 | 77.5 | 9.0 | 8.3 | 97 | 4.0 | 0.2 | 0.0 |
| 4 | 4 | 8 | 6 | mar | sun | 89.3 | 51.3 | 102.2 | 9.6 | 11.4 | 99 | 1.8 | 0.0 | 0.0 |
데이터 프레임에서 원하는 값을 다양한 방법으로 선택할 수 있다.
[] 안에 열의 이름을 넣으면 열만 선택할 수 있다.
df['month'].head()
0 mar
1 oct
2 oct
3 mar
4 mar
Name: month, dtype: object
하나의 열은 Series 형태로 나온다. 여기에서 Series 에서 작동하듯, indexing/slicing 을 통해 자료에 접근할 수 있다.
df['month'][0] # 열의 첫번째 값
'mar'
df['month'][:5] # 첫 다섯개 값
0 mar
1 oct
2 oct
3 mar
4 mar
Name: month, dtype: object
[[]] 를 사용해 하위 데이터 프레임을 만들 수 있다.
df[['month', 'day', 'rain']].head()
| month | day | rain | |
|---|---|---|---|
| 0 | mar | fri | 0.0 |
| 1 | oct | tue | 0.0 |
| 2 | oct | sat | 0.0 |
| 3 | mar | fri | 0.2 |
| 4 | mar | sun | 0.0 |
month, day, rain 의 열을 갖는 첫 10 행만 담는 데이터 프레임을 만들어보자.
df[['month', 'day', 'rain']][:10]
| month | day | rain | |
|---|---|---|---|
| 0 | mar | fri | 0.0 |
| 1 | oct | tue | 0.0 |
| 2 | oct | sat | 0.0 |
| 3 | mar | fri | 0.2 |
| 4 | mar | sun | 0.0 |
| 5 | aug | sun | 0.0 |
| 6 | aug | mon | 0.0 |
| 7 | aug | mon | 0.0 |
| 8 | sep | tue | 0.0 |
| 9 | sep | sat | 0.0 |
행에 접근할때는 .loc 이나 .iloc 을 사용한다. .loc 은 행의 이름으로 정보를 찾을때 사용한다. .iloc 은 행의 index 번호로 자료를 찾을 때 사용한다.
df.loc[0] # 행 이름이 0 인 자료 정보
Unnamed: 0 0
X 7
Y 5
month mar
day fri
FFMC 86.2
DMC 26.2
DC 94.3
ISI 5.1
temp 8.2
RH 51
wind 6.7
rain 0
area 0
Name: 0, dtype: object
df.iloc[0] # 첫 행 정보
Unnamed: 0 0
X 7
Y 5
month mar
day fri
FFMC 86.2
DMC 26.2
DC 94.3
ISI 5.1
temp 8.2
RH 51
wind 6.7
rain 0
area 0
Name: 0, dtype: object
뒤에 열 이름으로 색인해주면 한 자료에 접근할 수 있다.
df.iloc[0]['temp']
8.1999999999999993
이를 다르게 표현할 수 있다.
df.loc[0, 'temp']
8.1999999999999993
slicing 을 사용해 여러 값을 선택할 수 있다.
df.iloc[:3]['temp']
0 8.2
1 18.0
2 14.6
Name: temp, dtype: float64
.iloc에서 indexing 을 사용해 하위 데이터 프레임을 만들 수 있다.
df.iloc[:5] # 첫 다섯개 행으로 하위 데이터 프레임 만들기
| Unnamed: 0 | X | Y | month | day | FFMC | DMC | DC | ISI | temp | RH | wind | rain | area | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 7 | 5 | mar | fri | 86.2 | 26.2 | 94.3 | 5.1 | 8.2 | 51 | 6.7 | 0.0 | 0.0 |
| 1 | 1 | 7 | 4 | oct | tue | 90.6 | 35.4 | 669.1 | 6.7 | 18.0 | 33 | 0.9 | 0.0 | 0.0 |
| 2 | 2 | 7 | 4 | oct | sat | 90.6 | 43.7 | 686.9 | 6.7 | 14.6 | 33 | 1.3 | 0.0 | 0.0 |
| 3 | 3 | 8 | 6 | mar | fri | 91.7 | 33.3 | 77.5 | 9.0 | 8.3 | 97 | 4.0 | 0.2 | 0.0 |
| 4 | 4 | 8 | 6 | mar | sun | 89.3 | 51.3 | 102.2 | 9.6 | 11.4 | 99 | 1.8 | 0.0 | 0.0 |
행의 이름으로 하위 데이터 프레임을 만들고 싶을 경우, .loc[[]] 를 사용해 하위 데이터 프레임을 만들 수 있다.
df.loc[[0, 3, 5]] # 0, 3, 5 라는 이름을 가진 행을 가진 데이터 프레임
| Unnamed: 0 | X | Y | month | day | FFMC | DMC | DC | ISI | temp | RH | wind | rain | area | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 7 | 5 | mar | fri | 86.2 | 26.2 | 94.3 | 5.1 | 8.2 | 51 | 6.7 | 0.0 | 0.0 |
| 3 | 3 | 8 | 6 | mar | fri | 91.7 | 33.3 | 77.5 | 9.0 | 8.3 | 97 | 4.0 | 0.2 | 0.0 |
| 5 | 5 | 8 | 6 | aug | sun | 92.3 | 85.3 | 488.0 | 14.7 | 22.2 | 29 | 5.4 | 0.0 | 0.0 |
하나의 값에 접근할 때, .at를 사용하기도 한다.
df.at[0, 'month'] # 행의 이름이 0 이며 열의 이름이 month 인 정보
'mar'
현재 행의 이름(index)는 숫자로 되어있다. 숫자가 아닌 열의 정보로 행의 이름을 지정하고 싶다면, .set_index() 를 사용한다.
현재 index 는 .index 로 확인할 수 있다.
df.index # 0-517 까지의 숫자
RangeIndex(start=0, stop=517, step=1)
X 값을 index 로 지정해보자.
df.set_index('X').head()
| Unnamed: 0 | Y | month | day | FFMC | DMC | DC | ISI | temp | RH | wind | rain | area | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| X | |||||||||||||
| 7 | 0 | 5 | mar | fri | 86.2 | 26.2 | 94.3 | 5.1 | 8.2 | 51 | 6.7 | 0.0 | 0.0 |
| 7 | 1 | 4 | oct | tue | 90.6 | 35.4 | 669.1 | 6.7 | 18.0 | 33 | 0.9 | 0.0 | 0.0 |
| 7 | 2 | 4 | oct | sat | 90.6 | 43.7 | 686.9 | 6.7 | 14.6 | 33 | 1.3 | 0.0 | 0.0 |
| 8 | 3 | 6 | mar | fri | 91.7 | 33.3 | 77.5 | 9.0 | 8.3 | 97 | 4.0 | 0.2 | 0.0 |
| 8 | 4 | 6 | mar | sun | 89.3 | 51.3 | 102.2 | 9.6 | 11.4 | 99 | 1.8 | 0.0 | 0.0 |
index 가 지정되었다.
이를 df2에 저장하자.
df2 = df.set_index('X')
df2.head() # 확인
| Unnamed: 0 | Y | month | day | FFMC | DMC | DC | ISI | temp | RH | wind | rain | area | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| X | |||||||||||||
| 7 | 0 | 5 | mar | fri | 86.2 | 26.2 | 94.3 | 5.1 | 8.2 | 51 | 6.7 | 0.0 | 0.0 |
| 7 | 1 | 4 | oct | tue | 90.6 | 35.4 | 669.1 | 6.7 | 18.0 | 33 | 0.9 | 0.0 | 0.0 |
| 7 | 2 | 4 | oct | sat | 90.6 | 43.7 | 686.9 | 6.7 | 14.6 | 33 | 1.3 | 0.0 | 0.0 |
| 8 | 3 | 6 | mar | fri | 91.7 | 33.3 | 77.5 | 9.0 | 8.3 | 97 | 4.0 | 0.2 | 0.0 |
| 8 | 4 | 6 | mar | sun | 89.3 | 51.3 | 102.2 | 9.6 | 11.4 | 99 | 1.8 | 0.0 | 0.0 |
df를 확인해보면, .set_index가 적용되지 않았다.
df.head()
| Unnamed: 0 | X | Y | month | day | FFMC | DMC | DC | ISI | temp | RH | wind | rain | area | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 7 | 5 | mar | fri | 86.2 | 26.2 | 94.3 | 5.1 | 8.2 | 51 | 6.7 | 0.0 | 0.0 |
| 1 | 1 | 7 | 4 | oct | tue | 90.6 | 35.4 | 669.1 | 6.7 | 18.0 | 33 | 0.9 | 0.0 | 0.0 |
| 2 | 2 | 7 | 4 | oct | sat | 90.6 | 43.7 | 686.9 | 6.7 | 14.6 | 33 | 1.3 | 0.0 | 0.0 |
| 3 | 3 | 8 | 6 | mar | fri | 91.7 | 33.3 | 77.5 | 9.0 | 8.3 | 97 | 4.0 | 0.2 | 0.0 |
| 4 | 4 | 8 | 6 | mar | sun | 89.3 | 51.3 | 102.2 | 9.6 | 11.4 | 99 | 1.8 | 0.0 | 0.0 |
pandas의 많은 메써드는 원본 데이터 프레임에 변화를 자동으로 저장하지 않는다.
만약 같은 변수의 이름의 데이터 프레임에 가공을 하고 싶다면, inplace=True 옵션을 넘겨 항상 새로운 변수에 저장하지 않아도 되는 번거로움을 없앨 수 있다.
df.set_index('X', inplace=True)
df.head()
| Unnamed: 0 | Y | month | day | FFMC | DMC | DC | ISI | temp | RH | wind | rain | area | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| X | |||||||||||||
| 7 | 0 | 5 | mar | fri | 86.2 | 26.2 | 94.3 | 5.1 | 8.2 | 51 | 6.7 | 0.0 | 0.0 |
| 7 | 1 | 4 | oct | tue | 90.6 | 35.4 | 669.1 | 6.7 | 18.0 | 33 | 0.9 | 0.0 | 0.0 |
| 7 | 2 | 4 | oct | sat | 90.6 | 43.7 | 686.9 | 6.7 | 14.6 | 33 | 1.3 | 0.0 | 0.0 |
| 8 | 3 | 6 | mar | fri | 91.7 | 33.3 | 77.5 | 9.0 | 8.3 | 97 | 4.0 | 0.2 | 0.0 |
| 8 | 4 | 6 | mar | sun | 89.3 | 51.3 | 102.2 | 9.6 | 11.4 | 99 | 1.8 | 0.0 | 0.0 |
편의를 위해 df 를 원래대로 돌려놓자
df = pd.read_csv('forest.csv')
열 혹은 행의 순서를 바꾸고 싶을때 .reindex 를 사용한다.
df 의 컬럼을 month, day, X, Y 로 바꿔보자.
df.reindex(columns=['month', 'day', 'X', 'Y']).head()
| month | day | X | Y | |
|---|---|---|---|---|
| 0 | mar | fri | 7 | 5 |
| 1 | oct | tue | 7 | 4 |
| 2 | oct | sat | 7 | 4 |
| 3 | mar | fri | 8 | 6 |
| 4 | mar | sun | 8 | 6 |
행의 순서도 바꿀 수 있다. 행의 이름을 기준으로, 순서가 4, 3, 2, 1, 0 이 되도록 바꿔보자.
df.reindex(index=[4, 3, 2, 1, 0])
| Unnamed: 0 | X | Y | month | day | FFMC | DMC | DC | ISI | temp | RH | wind | rain | area | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 4 | 4 | 8 | 6 | mar | sun | 89.3 | 51.3 | 102.2 | 9.6 | 11.4 | 99 | 1.8 | 0.0 | 0.0 |
| 3 | 3 | 8 | 6 | mar | fri | 91.7 | 33.3 | 77.5 | 9.0 | 8.3 | 97 | 4.0 | 0.2 | 0.0 |
| 2 | 2 | 7 | 4 | oct | sat | 90.6 | 43.7 | 686.9 | 6.7 | 14.6 | 33 | 1.3 | 0.0 | 0.0 |
| 1 | 1 | 7 | 4 | oct | tue | 90.6 | 35.4 | 669.1 | 6.7 | 18.0 | 33 | 0.9 | 0.0 | 0.0 |
| 0 | 0 | 7 | 5 | mar | fri | 86.2 | 26.2 | 94.3 | 5.1 | 8.2 | 51 | 6.7 | 0.0 | 0.0 |
데이터 프레임 정렬에 대해 배워보자.
import pandas as pd
예제로 성인들의 인적 정보가 담긴 adult 예제를 살펴보자
df = pd.read_csv('adult.csv', header=None)
이 자료는 header 가 없기 때문에, 따로 열 이름을 지정해줘야 한다. 자세한 사항은 rename 강의에 나와있다.
titles = {0: '나이', 1: '고용형태', 2: '가중치',
3: '최종학위', 4:'교육햇수', 5: '결혼유무',
6: '직업명', 7:'가족내역할', 8:'인종',
9: '성별', 10: '자본이득', 11: '자본손실',
12: '노동시간', 13:'고향', 14:'수입'
}
df.rename(columns=titles, inplace=True)
df.head()
| 나이 | 고용형태 | 가중치 | 최종학위 | 교육햇수 | 결혼유무 | 직업명 | 가족내역할 | 인종 | 성별 | 자본이득 | 자본손실 | 노동시간 | 고향 | 수입 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 39 | State-gov | 77516 | Bachelors | 13 | Never-married | Adm-clerical | Not-in-family | White | Male | 2174 | 0 | 40 | United-States | <=50K |
| 1 | 50 | Self-emp-not-inc | 83311 | Bachelors | 13 | Married-civ-spouse | Exec-managerial | Husband | White | Male | 0 | 0 | 13 | United-States | <=50K |
| 2 | 38 | Private | 215646 | HS-grad | 9 | Divorced | Handlers-cleaners | Not-in-family | White | Male | 0 | 0 | 40 | United-States | <=50K |
| 3 | 53 | Private | 234721 | 11th | 7 | Married-civ-spouse | Handlers-cleaners | Husband | Black | Male | 0 | 0 | 40 | United-States | <=50K |
| 4 | 28 | Private | 338409 | Bachelors | 13 | Married-civ-spouse | Prof-specialty | Wife | Black | Female | 0 | 0 | 40 | Cuba | <=50K |
일단, 나이 순으로 자료를 정렬해보자.
정렬은 .sort_values() 를 이용한다.
df.sort_values('나이').head()
| 나이 | 고용형태 | 가중치 | 최종학위 | 교육햇수 | 결혼유무 | 직업명 | 가족내역할 | 인종 | 성별 | 자본이득 | 자본손실 | 노동시간 | 고향 | 수입 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 12318 | 17 | Private | 127366 | 11th | 7 | Never-married | Sales | Own-child | White | Female | 0 | 0 | 8 | United-States | <=50K |
| 6312 | 17 | Private | 132755 | 11th | 7 | Never-married | Sales | Own-child | White | Male | 0 | 0 | 15 | United-States | <=50K |
| 30927 | 17 | Private | 108470 | 11th | 7 | Never-married | Other-service | Own-child | Black | Male | 0 | 0 | 17 | United-States | <=50K |
| 12787 | 17 | Local-gov | 308901 | 11th | 7 | Never-married | Adm-clerical | Own-child | White | Female | 0 | 0 | 15 | United-States | <=50K |
| 25755 | 17 | NaN | 47407 | 11th | 7 | Never-married | NaN | Own-child | White | Male | 0 | 0 | 10 | United-States | <=50K |
나이가 적은 순으로 정렬되었다.
정렬 순서를 바꾸고 싶다면 ascending=False 옵션을 지정해주면 된다.
ascending 의 초기값은 True로 오름차순이 기본 설정이다.
df.sort_values('나이', ascending=False).head()
| 나이 | 고용형태 | 가중치 | 최종학위 | 교육햇수 | 결혼유무 | 직업명 | 가족내역할 | 인종 | 성별 | 자본이득 | 자본손실 | 노동시간 | 고향 | 수입 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 5406 | 90 | Private | 51744 | Masters | 14 | Never-married | Exec-managerial | Not-in-family | Black | Male | 0 | 0 | 50 | United-States | >50K |
| 6624 | 90 | Private | 313986 | 11th | 7 | Married-civ-spouse | Craft-repair | Husband | White | Male | 0 | 0 | 40 | United-States | <=50K |
| 20610 | 90 | Private | 206667 | Masters | 14 | Married-civ-spouse | Prof-specialty | Wife | White | Female | 0 | 0 | 40 | United-States | >50K |
| 1040 | 90 | Private | 137018 | HS-grad | 9 | Never-married | Other-service | Not-in-family | White | Female | 0 | 0 | 40 | United-States | <=50K |
| 1935 | 90 | Private | 221832 | Bachelors | 13 | Married-civ-spouse | Exec-managerial | Husband | White | Male | 0 | 0 | 45 | United-States | <=50K |
나이가 많은 순으로 정렬 되었다.
한번에 여러 열을 정렬할 수 있다.
0번 열의 나이는 내림차순, 4번 열의 교육 햇수는 오름차순으로 정렬해보자.
df.sort_values(['나이', '교육햇수'], ascending=[False, True]).head()
| 나이 | 고용형태 | 가중치 | 최종학위 | 교육햇수 | 결혼유무 | 직업명 | 가족내역할 | 인종 | 성별 | 자본이득 | 자본손실 | 노동시간 | 고향 | 수입 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 24238 | 90 | NaN | 166343 | 1st-4th | 2 | Widowed | NaN | Not-in-family | Black | Female | 0 | 0 | 40 | United-States | <=50K |
| 19747 | 90 | Private | 226968 | 7th-8th | 4 | Married-civ-spouse | Machine-op-inspct | Husband | White | Male | 0 | 0 | 40 | United-States | <=50K |
| 25303 | 90 | NaN | 175444 | 7th-8th | 4 | Separated | NaN | Not-in-family | White | Female | 0 | 0 | 15 | United-States | <=50K |
| 32367 | 90 | Local-gov | 214594 | 7th-8th | 4 | Married-civ-spouse | Protective-serv | Husband | White | Male | 2653 | 0 | 40 | United-States | <=50K |
| 5272 | 90 | Private | 141758 | 9th | 5 | Never-married | Adm-clerical | Not-in-family | White | Female | 0 | 0 | 40 | United-States | <=50K |
나이는 많지만 교육 햇수는 적은 순으로 정렬되었다.
위에서는 값을 기준으로 정렬시켰지만, 이번에는 행의 이름을 기준으로 정렬을 해보자.
먼저 6번 직업명의 열을 행이름으로 설정하자.
df.set_index('직업명').head()
| 나이 | 고용형태 | 가중치 | 최종학위 | 교육햇수 | 결혼유무 | 가족내역할 | 인종 | 성별 | 자본이득 | 자본손실 | 노동시간 | 고향 | 수입 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 직업명 | ||||||||||||||
| Adm-clerical | 39 | State-gov | 77516 | Bachelors | 13 | Never-married | Not-in-family | White | Male | 2174 | 0 | 40 | United-States | <=50K |
| Exec-managerial | 50 | Self-emp-not-inc | 83311 | Bachelors | 13 | Married-civ-spouse | Husband | White | Male | 0 | 0 | 13 | United-States | <=50K |
| Handlers-cleaners | 38 | Private | 215646 | HS-grad | 9 | Divorced | Not-in-family | White | Male | 0 | 0 | 40 | United-States | <=50K |
| Handlers-cleaners | 53 | Private | 234721 | 11th | 7 | Married-civ-spouse | Husband | Black | Male | 0 | 0 | 40 | United-States | <=50K |
| Prof-specialty | 28 | Private | 338409 | Bachelors | 13 | Married-civ-spouse | Wife | Black | Female | 0 | 0 | 40 | Cuba | <=50K |
이를 df2라고 저장하자.
df2 = df.set_index('직업명')
이제 행 이름을 기준으로 정렬해보자. 행 이름을 기준으로 정렬은 .sort_index()를 사용한다.
df2.sort_index().head()
| 나이 | 고용형태 | 가중치 | 최종학위 | 교육햇수 | 결혼유무 | 가족내역할 | 인종 | 성별 | 자본이득 | 자본손실 | 노동시간 | 고향 | 수입 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 직업명 | ||||||||||||||
| Adm-clerical | 39 | State-gov | 77516 | Bachelors | 13 | Never-married | Not-in-family | White | Male | 2174 | 0 | 40 | United-States | <=50K |
| Adm-clerical | 18 | Private | 210932 | HS-grad | 9 | Never-married | Own-child | White | Female | 0 | 0 | 40 | United-States | <=50K |
| Adm-clerical | 20 | Private | 159297 | Some-college | 10 | Never-married | Own-child | Asian-Pac-Islander | Female | 0 | 0 | 15 | United-States | <=50K |
| Adm-clerical | 26 | Private | 202203 | Bachelors | 13 | Never-married | Other-relative | White | Female | 0 | 0 | 50 | United-States | <=50K |
| Adm-clerical | 32 | Private | 211028 | Some-college | 10 | Never-married | Not-in-family | White | Female | 0 | 0 | 40 | United-States | <=50K |
직업명 기준으로 정렬되었다.
데이터 프레임에서 원하는 조건의 값만 필터링 해보자.
예제 데이터는 고객의 은행 신상 정보다.
import pandas as pd
bank = pd.read_csv('bank.csv', sep=';')
bank.head()
| age | job | marital | education | default | balance | housing | loan | contact | day | month | duration | campaign | pdays | previous | poutcome | y | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 30 | unemployed | married | primary | no | 1787 | no | no | cellular | 19 | oct | 79 | 1 | -1 | 0 | unknown | no |
| 1 | 33 | services | married | secondary | no | 4789 | yes | yes | cellular | 11 | may | 220 | 1 | 339 | 4 | failure | no |
| 2 | 35 | management | single | tertiary | no | 1350 | yes | no | cellular | 16 | apr | 185 | 1 | 330 | 1 | failure | no |
| 3 | 30 | management | married | tertiary | no | 1476 | yes | yes | unknown | 3 | jun | 199 | 4 | -1 | 0 | unknown | no |
| 4 | 59 | blue-collar | married | secondary | no | 0 | yes | no | unknown | 5 | may | 226 | 1 | -1 | 0 | unknown | no |
자료는 4,521개의 행과 17개의 열로 이루어진다.
bank.shape
(4521, 17)
비교연산자를 사용해 일정 조건에 충족하는 값만 추출해보자.
나이가 35세 이전인 값만 추출해보자.
under_35 변수에 이를 저장해보겠다.
under_35 = bank['age'] < 35
under_35.head()
0 True
1 True
2 False
3 True
4 False
Name: age, dtype: bool
Series 안에 불리안 형태로 결과가 출력된다.
young = bank[under_35]
young.head()
| age | job | marital | education | default | balance | housing | loan | contact | day | month | duration | campaign | pdays | previous | poutcome | y | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 30 | unemployed | married | primary | no | 1787 | no | no | cellular | 19 | oct | 79 | 1 | -1 | 0 | unknown | no |
| 1 | 33 | services | married | secondary | no | 4789 | yes | yes | cellular | 11 | may | 220 | 1 | 339 | 4 | failure | no |
| 3 | 30 | management | married | tertiary | no | 1476 | yes | yes | unknown | 3 | jun | 199 | 4 | -1 | 0 | unknown | no |
| 13 | 20 | student | single | secondary | no | 502 | no | no | cellular | 30 | apr | 261 | 1 | -1 | 0 | unknown | yes |
| 14 | 31 | blue-collar | married | secondary | no | 360 | yes | yes | cellular | 29 | jan | 89 | 1 | 241 | 1 | failure | no |
young의 shape를 확인해보니, 행의 갯수가 1,472개로 줄었다.
young.shape
(1472, 17)
따라서 young은 나이가 35세 이전인 사람들의 자료만 모아놓은 데이터 프레임이다.
이를 한번에 표현하면 다음과 같다.
young = bank[bank['age'] < 35]
young.shape
(1472, 17)
이번에는 marital 이 'married' 인 사람들만 추출해보자.
marital 열이 'married' 와 같다면, 이를 따로 모아 데이터 프레임을 만들자.
married = bank[bank['marital'] == 'married']
married.head()
| age | job | marital | education | default | balance | housing | loan | contact | day | month | duration | campaign | pdays | previous | poutcome | y | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 30 | unemployed | married | primary | no | 1787 | no | no | cellular | 19 | oct | 79 | 1 | -1 | 0 | unknown | no |
| 1 | 33 | services | married | secondary | no | 4789 | yes | yes | cellular | 11 | may | 220 | 1 | 339 | 4 | failure | no |
| 3 | 30 | management | married | tertiary | no | 1476 | yes | yes | unknown | 3 | jun | 199 | 4 | -1 | 0 | unknown | no |
| 4 | 59 | blue-collar | married | secondary | no | 0 | yes | no | unknown | 5 | may | 226 | 1 | -1 | 0 | unknown | no |
| 6 | 36 | self-employed | married | tertiary | no | 307 | yes | no | cellular | 14 | may | 341 | 1 | 330 | 2 | other | no |
필터링으로 인해 행의 수가 줄어든것을 볼 수 있다. 이제는 결혼한 사람들만 선택되었다.
married.shape
(2797, 17)
이번에는 두가지 조건을 모두 만족하는 값을 추출해보자. 두 조건이 모두 충족하는 값을 찾을때는 &를 사용한다.
이를 young_married에 저장해보자.
young_married = bank[(bank['marital'] == 'married') &
(bank['age'] < 35)]
young_married.head()
| age | job | marital | education | default | balance | housing | loan | contact | day | month | duration | campaign | pdays | previous | poutcome | y | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 30 | unemployed | married | primary | no | 1787 | no | no | cellular | 19 | oct | 79 | 1 | -1 | 0 | unknown | no |
| 1 | 33 | services | married | secondary | no | 4789 | yes | yes | cellular | 11 | may | 220 | 1 | 339 | 4 | failure | no |
| 3 | 30 | management | married | tertiary | no | 1476 | yes | yes | unknown | 3 | jun | 199 | 4 | -1 | 0 | unknown | no |
| 14 | 31 | blue-collar | married | secondary | no | 360 | yes | yes | cellular | 29 | jan | 89 | 1 | 241 | 1 | failure | no |
| 19 | 31 | services | married | secondary | no | 132 | no | no | cellular | 7 | jul | 148 | 1 | 152 | 1 | other | no |
35세 이하의 기혼인 사람만 선택되었다.
young_married.shape
(639, 17)
이번에는 두가지 조건 중 하나만 충족하는 값을 추출해보자. 두 조건 중 하나만 충족하는 값을 찾을때는 | 를 사용한다.
young_married = bank[(bank['marital'] == 'married') |
(bank['age'] < 35)]
young_married.head()
| age | job | marital | education | default | balance | housing | loan | contact | day | month | duration | campaign | pdays | previous | poutcome | y | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 30 | unemployed | married | primary | no | 1787 | no | no | cellular | 19 | oct | 79 | 1 | -1 | 0 | unknown | no |
| 1 | 33 | services | married | secondary | no | 4789 | yes | yes | cellular | 11 | may | 220 | 1 | 339 | 4 | failure | no |
| 3 | 30 | management | married | tertiary | no | 1476 | yes | yes | unknown | 3 | jun | 199 | 4 | -1 | 0 | unknown | no |
| 4 | 59 | blue-collar | married | secondary | no | 0 | yes | no | unknown | 5 | may | 226 | 1 | -1 | 0 | unknown | no |
| 6 | 36 | self-employed | married | tertiary | no | 307 | yes | no | cellular | 14 | may | 341 | 1 | 330 | 2 | other | no |
35세 이하이거나 결혼한 사람들이 선택되었다.
young.shape
(1472, 17)
pandas 에서 손쉽게 열에 대해 통계 정보를 얻을 수 있다.
예제는 포르투갈에서 발생한 산불에 대한 정보이다.
import pandas as pd
df = pd.read_excel('forestfires.xlsx')
df.head() # d f 를 확인하겠습니다
| X | Y | month | day | FFMC | DMC | DC | ISI | temp | RH | wind | rain | area | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 7 | 5 | mar | fri | 86.2 | 26.2 | 94.3 | 5.1 | 8.2 | 51 | 6.7 | 0.0 | 0.0 |
| 1 | 7 | 4 | oct | tue | 90.6 | 35.4 | 669.1 | 6.7 | 18.0 | 33 | 0.9 | 0.0 | 0.0 |
| 2 | 7 | 4 | oct | sat | 90.6 | 43.7 | 686.9 | 6.7 | 14.6 | 33 | 1.3 | 0.0 | 0.0 |
| 3 | 8 | 6 | mar | fri | 91.7 | 33.3 | 77.5 | 9.0 | 8.3 | 97 | 4.0 | 0.2 | 0.0 |
| 4 | 8 | 6 | mar | sun | 89.3 | 51.3 | 102.2 | 9.6 | 11.4 | 99 | 1.8 | 0.0 | 0.0 |
X, Y 는 발생 좌표를 의미하며, month, day 는 발생 달 과, 요일을 의미한다.
그 외에도 다양한 건조주의보 수치, 온도, 습도, 강수량 등의 날씨 정보와, 피해 규모에 대한 정보를 담고 있다.
데이터 프레임에서 원하는 열을 선택한 후, 뒤에 원하는 메써드를 부르는 방법으로 통계 값을 구할 수 있다. .mean()으로 평균 강수량을 살펴보자.
df['rain'].mean()
0.02166344294003869
.sum()으로 총 면적을 구해보자.
df['area'].sum()
6642.049999999998
.median()으로 X 좌표의 중앙값을 구해보자.
df['X'].median()
4.0
.mode()으로 Y 좌표의 최빈값을 구해보자.
df['Y'].mode()
0 4
dtype: int64
.std()로 습도의 표준편차를 구해보자.
df['RH'].std()
16.317469239378394
이번에는 같은 방식으로 최고, 최저 값 등을 구해보자.
온도의 최소값을 구해보자.
df['temp'].min()
2.2000000000000002
온도의 최고값을 구해보자.
df['temp'].max()
33.299999999999997
최저 온도의 행 위치를 보고 싶을때는 .idxmin()을 사용한다.
df['temp'].idxmin()
280
280 번째 행에 최저값이 위치해있다.
최고 온도의 행 위치를 보자.
df['temp'].idxmax()
498
.unique() 는 등장한 값의 종류를 보여준다.
어떤 요일에 산불이 났는지 확인해보자.
df['day'].unique()
array(['fri', 'tue', 'sat', 'sun', 'mon', 'wed', 'thu'], dtype=object)
확인해보니, 모든 요일에 산불이 한번씩 났다.
.nunique()함수는 몇개의 값의 종류가 있는지 보여준다.
요일에 대한 값의 종류가 몇개인지 구해보자.
df['day'].nunique()
7
산불이 발생한 요일은 총 7개임을 알 수 있다.
value_counts()로 각 값의 출현 빈도를 구할 수 있다.
어느 달에 산불이 가장 많이 발생했는지 확인해보자.
df['month'].value_counts()
aug 184
sep 172
mar 54
jul 32
feb 20
jun 17
oct 15
apr 9
dec 9
may 2
jan 2
nov 1
Name: month, dtype: int64
확인결과, 8월과 9월에 산불이 가장 많이 발생했다.
주피터 노트북에서 시각화를 할 때는 항상 아래 명령을 입력해서 그래프가 노트북 안에 포함되도록 한다.
%matplotlib inline
seaborn은 Python 시각화에 가장 많이 사용되는 라이브러리 중에 하나이다. seaborn은 matplotlib를 기반으로 한다.
import seaborn as sns
from matplotlib import pyplot as plt
실습에 사용할 데이터는 iris 데이터이다. iris 데이터는 seaborn에 내장되어 있어 아래 명령으로 불러올 수 있다.
df = sns.load_dataset('iris')
위의 명령이 안될 경우 https://raw.githubusercontent.com/mwaskom/seaborn-data/master/iris.csv 에서 다운 받은 다음 pandas로 직접 읽어들인다.
import pandas
df = pandas.read_csv('iris.csv')
어떤 방법으로든 데이터를 불러왔으면 데이터의 내용을 확인해보자.
df.head()
| sepal_length | sepal_width | petal_length | petal_width | species | |
|---|---|---|---|---|---|
| 0 | 5.1 | 3.5 | 1.4 | 0.2 | setosa |
| 1 | 4.9 | 3.0 | 1.4 | 0.2 | setosa |
| 2 | 4.7 | 3.2 | 1.3 | 0.2 | setosa |
| 3 | 4.6 | 3.1 | 1.5 | 0.2 | setosa |
| 4 | 5.0 | 3.6 | 1.4 | 0.2 | setosa |
iris는 150 송이 붓꽃의 데이터이다. sepal은 꽃잎, petal은 꽃받침, species는 품종을 뜻한다.
먼저 꽃잎의 길이와 꽃받침의 길이를 산점도로 나타내보자.
sns.regplot(x=df["sepal_length"], y=df["petal_length"])
<matplotlib.axes._subplots.AxesSubplot at 0x29d58b7bf28>
<matplotlib.figure.Figure at 0x29d588d1b00>
색상을 빨간색으로 바꿔보자.
sns.regplot(x=df["sepal_length"], y=df["petal_length"], color='red')
<matplotlib.axes._subplots.AxesSubplot at 0x29d58bb0470>
<matplotlib.figure.Figure at 0x29d58b6d470>
선의 속성만 바꾸려면 line_kws에 설정값을 사전 형태로 넘겨준다.
sns.regplot(x=df["sepal_length"], y=df["petal_length"], line_kws={'color': 'red'})
<matplotlib.axes._subplots.AxesSubplot at 0x29d58c21a20>
<matplotlib.figure.Figure at 0x29d58c5ea20>
점의 속성만 바꾸려면 scatter_kws에 설정값을 사전 형태로 넘겨준다.
sns.regplot(x=df["sepal_length"], y=df["petal_length"], scatter_kws={'color': 'red'})
<matplotlib.axes._subplots.AxesSubplot at 0x29d585f7710>
<matplotlib.figure.Figure at 0x29d58cde198>
색상에는 색상 코드를 사용할 수 있다. 색상 코드는 구글에서 "color picker"를 검색하자.
sns.regplot(x=df["sepal_length"], y=df["petal_length"], color='#f49842')
<matplotlib.axes._subplots.AxesSubplot at 0x29d58d305f8>
<matplotlib.figure.Figure at 0x29d58d50630>
fit_reg 옵션으로 회귀선을 제거할 수 있다.
sns.regplot(x=df["sepal_length"], y=df["petal_length"], fit_reg=False)
<matplotlib.axes._subplots.AxesSubplot at 0x29d58d6bba8>
<matplotlib.figure.Figure at 0x29d58dab710>
자세한 내용은 공식 문서를 참고하자. https://seaborn.pydata.org/generated/seaborn.regplot.html
line_kws와 scatter_kws에 넘길 수 있는 옵션은 다음 링크를 참고하자.
matplotlib에는 피겨(figure)와 액시즈(axes)라는 개념이 있다. 피겨는 그림을 가리키고, 액시즈는 그림에 포함된 하나의 그래프를 가리킨다.
seaborn의 그래프 함수들은 해당 그래프의 액시즈를 반환한다. 만약 그래프의 속성을 바꾸고 싶으면 해당 액시즈를 이용한다.
자세한 내용은 matplotlib의 https://matplotlib.org/api/axes_api.html 를 참고한다_api.html 를 참고한다.
ax = sns.regplot(x=df["sepal_length"], y=df["petal_length"])
ax.set_ylim([0, 8])
(0, 8)
<matplotlib.figure.Figure at 0x29d58e341d0>
축 이름은 set_xlabel과 set_ylabel로 붙인다.
ax.set_xlabel('Sepal Length')
ax.figure # 그림을 현재 셀에서 다시 보여준다
<matplotlib.figure.Figure at 0x29d58e341d0>
한글을 사용하면 글자가 깨진다.
ax.set_xlabel('꽃잎 길이')
ax.figure
<matplotlib.figure.Figure at 0x29d58e341d0>
다음과 같이 글꼴을 한글 글꼴로 바꿔준다.
plt.rc('font', family='Malgun Gothic')
다시 그래프를 그리면 한글이 잘 표시된다.
ax = sns.regplot(x=df["sepal_length"], y=df["petal_length"])
ax.set_xlabel('꽃잎 길이')
ax.set_ylabel('꽃받침 길이')
<matplotlib.text.Text at 0x29d58e93da0>
<matplotlib.figure.Figure at 0x29d58dd0be0>
위의 그래프를 자세히보면 왼쪽 아래 마이너스 표시가 깨지는 것을 볼 수 있다. 이것은 한글 폰트에 마이너스가 없기 때문에 생기는 현상이다. 다음과 같이 해주면 된다.
plt.rc('font', family='Malgun Gothic')
plt.rc('axes', unicode_minus=False)
ax = sns.regplot(x=df["sepal_length"], y=df["petal_length"])
ax.set_xlabel('꽃잎 길이')
ax.set_ylabel('꽃받침 길이')
<matplotlib.text.Text at 0x29d58f09a90>
<matplotlib.figure.Figure at 0x29d59ef45f8>
글꼴 이름이 무엇으로 되어있는지 모르는 경우 fontManager를 사용한다.
from matplotlib.font_manager import fontManager
아래와 같이 글꼴 이름에 Nanum이 들어가는 경우를 찾는다.
for font in fontManager.ttflist:
if 'Nanum' in font.name:
print(font.name)
ax.figure를 통해 그림 속성을 바꿀 수도 있다.
matplotlib의 Figure 문서 참고
ax = sns.regplot(x=df["sepal_length"], y=df["petal_length"])
ax.figure.set_size_inches(12, 10)
<matplotlib.figure.Figure at 0x1678fb70630>
그림을 저장하는 것도 피겨의 기능이다.
ax = sns.regplot(x=df["sepal_length"], y=df["petal_length"])
ax.figure.savefig('lm.png')
색상 등 그래프의 스타일을 조정할 경우 시본의 스타일 설정 기능을 사용한다. 자세한 내용은 https://seaborn.pydata.org/tutorial/aesthetics.html 문서를 참고한다.
sns.set_style('darkgrid')
sns.regplot(x=df["sepal_length"], y=df["petal_length"])
<matplotlib.axes._subplots.AxesSubplot at 0x1678ff0d908>
<matplotlib.figure.Figure at 0x1678fddac88>
다음 명령으로 기본 스타일로 돌아올 수 있다.
sns.set()
산점도 두 변수의 관계를 보여준다면 히스토그램과 밀도 그래프는 한 변수의 분포를 보여준다.
sns.distplot(df["sepal_width"])
<matplotlib.axes._subplots.AxesSubplot at 0x1678fa803c8>
<matplotlib.figure.Figure at 0x1678fe6be80>
bins 옵션으로 막대의 갯수를 조절할 수 있다.
sns.distplot(df["sepal_width"], bins=4)
<matplotlib.axes._subplots.AxesSubplot at 0x16790066978>
<matplotlib.figure.Figure at 0x167900b34a8>
또는 구간을 명시할 수도 있다.
sns.distplot(df["sepal_width"], bins=[2, 2.5, 3, 3.5, 4, 4.5, 5])
<matplotlib.axes._subplots.AxesSubplot at 0x1679015a550>
<matplotlib.figure.Figure at 0x1679117ceb8>
numpy.arange를 사용하면 일정 구간으로 막대를 만들 수도 있다.
import numpy
sns.distplot(df["sepal_width"], bins=numpy.arange(2, 5.5, 0.5))
<matplotlib.axes._subplots.AxesSubplot at 0x16791187470>
<matplotlib.figure.Figure at 0x167911ff780>
kde=False을 넘겨주면 밀도 그래프를 그리지 않는다.
sns.distplot(df["sepal_width"], kde=False)
<matplotlib.axes._subplots.AxesSubplot at 0x1678fa9ff60>
<matplotlib.figure.Figure at 0x1678fbd8898>
hist=False을 넘겨주면 히스토그램을 그리지 않는다.
sns.distplot(df["sepal_width"], hist=False)
<matplotlib.axes._subplots.AxesSubplot at 0x1678ff8be10>
<matplotlib.figure.Figure at 0x1678fe3ccc0>
자세한 내용은 https://seaborn.pydata.org/generated/seaborn.distplot.html 를 참고하자.
파이썬 연산 기호로 열끼리 간단한 계산을 할 수 있다.
예제로 성인 인적사항 자료를 사용하자.
import pandas as pd
df = pd.read_csv('adult.csv', header=None)
titles = {0: '나이', 1: '고용형태', 2: '가중치',
3: '최종학위', 4:'교육햇수', 5: '결혼유무',
6: '직업명', 7:'가족내역할', 8:'인종',
9: '성별', 10: '자본이득', 11: '자본손실',
12: '노동시간', 13:'고향', 14:'수입'
}
df.rename(columns=titles, inplace=True)
df.head()
| 나이 | 고용형태 | 가중치 | 최종학위 | 교육햇수 | 결혼유무 | 직업명 | 가족내역할 | 인종 | 성별 | 자본이득 | 자본손실 | 노동시간 | 고향 | 수입 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 39 | State-gov | 77516 | Bachelors | 13 | Never-married | Adm-clerical | Not-in-family | White | Male | 2174 | 0 | 40 | United-States | <=50K |
| 1 | 50 | Self-emp-not-inc | 83311 | Bachelors | 13 | Married-civ-spouse | Exec-managerial | Husband | White | Male | 0 | 0 | 13 | United-States | <=50K |
| 2 | 38 | Private | 215646 | HS-grad | 9 | Divorced | Handlers-cleaners | Not-in-family | White | Male | 0 | 0 | 40 | United-States | <=50K |
| 3 | 53 | Private | 234721 | 11th | 7 | Married-civ-spouse | Handlers-cleaners | Husband | Black | Male | 0 | 0 | 40 | United-States | <=50K |
| 4 | 28 | Private | 338409 | Bachelors | 13 | Married-civ-spouse | Prof-specialty | Wife | Black | Female | 0 | 0 | 40 | Cuba | <=50K |
열에 상수를 연산할 수 있다. 판다스는 각 행에 해당 연산을 적용시켜 돌려준다.
한 달 동안 근무 시간을 보기 위해 주당 근무 시간에 4 를 곱해보자.
hours_per_month = df['노동시간'] * 4
결과를 확인해보니, 월별 근무시간이 계산되었다.
hours_per_month.head()
0 160
1 52
2 160
3 160
4 160
Name: 노동시간, dtype: int64
반대로, 일당 근무 시간을 알아보기 위해 노동시간을 5 로 나누자.
hours_per_day = df['노동시간'] / 5
확인결과 일당 노동시간이 구해졌다.
hours_per_day.head()
0 8.0
1 2.6
2 8.0
3 8.0
4 8.0
Name: 노동시간, dtype: float64
두번째 사람을 제외하고 첫 다섯 사람은 하루 8시간 근무한다.
서로 다른 열을 사용해 계산할 수 있다.
- 로 자본이익에서 자본손실을 빼 true_gain에 저장하자.
true_gain = df['자본이득'] - df['자본손실']
확인해보니 차액이 구해졌다.
true_gain.head()
0 2174
1 0
2 0
3 0
4 0
dtype: int64
열과 열을 계산하며 상수를 포함해서 연산을 할 수 있다.
나이에서 교육받은 햇수와 유아기 8년을 빼, 근무 햇수를 구해보자.
not_educated = df['나이'] - df['교육햇수'] - 8
확인해보니, 모든 열에서 상수가 뺄셈되었다.
not_educated.head()
0 18
1 29
2 21
3 38
4 7
dtype: int64
이번 강의에서는 .groupby를 이용해 각 열의 값을 기준으로 데이터를 보는 방법을 살펴보자.
import pandas as pd
df = pd.read_csv('adult.csv', skipinitialspace=True, header=None) # 값 앞에 공백이 포함되어 있기 때문에 제거해야 함
titles = {0: '나이', 1: '고용형태', 2: '가중치',
3: '최종학위', 4:'교육년수', 5: '결혼유무',
6: '직업명', 7:'가족내역할', 8:'인종',
9: '성별', 10: '자본이득', 11: '자본손실',
12: '근무시간', 13:'고향', 14:'수입'
}
df.rename(columns=titles, inplace=True)
df.head()
| 나이 | 고용형태 | 가중치 | 최종학위 | 교육년수 | 결혼유무 | 직업명 | 가족내역할 | 인종 | 성별 | 자본이득 | 자본손실 | 근무시간 | 고향 | 수입 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 39 | State-gov | 77516 | Bachelors | 13 | Never-married | Adm-clerical | Not-in-family | White | Male | 2174 | 0 | 40 | United-States | <=50K |
| 1 | 50 | Self-emp-not-inc | 83311 | Bachelors | 13 | Married-civ-spouse | Exec-managerial | Husband | White | Male | 0 | 0 | 13 | United-States | <=50K |
| 2 | 38 | Private | 215646 | HS-grad | 9 | Divorced | Handlers-cleaners | Not-in-family | White | Male | 0 | 0 | 40 | United-States | <=50K |
| 3 | 53 | Private | 234721 | 11th | 7 | Married-civ-spouse | Handlers-cleaners | Husband | Black | Male | 0 | 0 | 40 | United-States | <=50K |
| 4 | 28 | Private | 338409 | Bachelors | 13 | Married-civ-spouse | Prof-specialty | Wife | Black | Female | 0 | 0 | 40 | Cuba | <=50K |
groupby 는 선택한 열의 그룹별로 자료를 분류한다.
예를 들어보기 위해 '가족내역할' 열의 값들을 살펴보자.
df['가족내역할'].unique()
array(['Not-in-family', 'Husband', 'Wife', 'Own-child', 'Unmarried',
'Other-relative'], dtype=object)
가족이 없는 사람, 남편, 부인, 자식, 미혼 등등의 값을 갖는다.
groupby 를 적용시켜보자.
df.groupby('가족내역할')
<pandas.core.groupby.DataFrameGroupBy object at 0x0000012DAE0CB780>
아직까지는 자료를 볼 수 없다
이를 grouped에 저장하자.
grouped = df.groupby('가족내역할')
.groups로 전체 그룹과 데이터 프레임에서 위치를 살펴 볼 수 있다. 결과물은 사전과 같은 형태로 나온다.
grouped.groups
{'Husband': Int64Index([ 1, 3, 7, 9, 10, 11, 14, 15, 18,
20,
...
32532, 32533, 32539, 32542, 32547, 32550, 32551, 32552, 32554,
32557],
dtype='int64', length=13193),
'Not-in-family': Int64Index([ 0, 2, 6, 8, 13, 28, 30, 44, 49,
53,
...
32523, 32531, 32536, 32537, 32541, 32544, 32546, 32548, 32553,
32555],
dtype='int64', length=8305),
'Other-relative': Int64Index([ 74, 110, 144, 152, 159, 195, 198, 233, 317,
335,
...
32306, 32322, 32359, 32387, 32398, 32401, 32477, 32499, 32524,
32549],
dtype='int64', length=981),
'Own-child': Int64Index([ 12, 16, 26, 31, 32, 33, 36, 51, 69,
70,
...
32486, 32488, 32492, 32496, 32511, 32512, 32527, 32535, 32540,
32559],
dtype='int64', length=5068),
'Unmarried': Int64Index([ 17, 19, 21, 24, 35, 43, 47, 91, 92,
98,
...
32493, 32500, 32516, 32520, 32525, 32529, 32534, 32538, 32543,
32558],
dtype='int64', length=3446),
'Wife': Int64Index([ 4, 5, 37, 50, 52, 67, 82, 93, 113,
125,
...
32425, 32428, 32479, 32485, 32513, 32528, 32530, 32545, 32556,
32560],
dtype='int64', length=1568)}
이중에서 Husband 를 색인해보자.
grouped.groups['Husband']
Int64Index([ 1, 3, 7, 9, 10, 11, 14, 15, 18,
20,
...
32532, 32533, 32539, 32542, 32547, 32550, 32551, 32552, 32554,
32557],
dtype='int64', length=13193)
1, 3, 7, 인덱스를 가진 행들이 '가족내역할' 중에서 Husband 의 값을 가진다는 것을 알 수 있다.
Husband 의 값을 가지는 값이 몇개인지 알아보자.
len(df[df['가족내역할'] == 'Husband'])
13193
자료가 어떻게 생겼는지 감을 잡기 위해, .first() 로 각 그룹마다 첫 자료를 열람해보자.
grouped.first()
| 나이 | 고용형태 | 가중치 | 최종학위 | 교육년수 | 결혼유무 | 직업명 | 인종 | 성별 | 자본이득 | 자본손실 | 근무시간 | 고향 | 수입 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 가족내역할 | ||||||||||||||
| Husband | 50 | Self-emp-not-inc | 83311 | Bachelors | 13 | Married-civ-spouse | Exec-managerial | White | Male | 0 | 0 | 13 | United-States | <=50K |
| Not-in-family | 39 | State-gov | 77516 | Bachelors | 13 | Never-married | Adm-clerical | White | Male | 2174 | 0 | 40 | United-States | <=50K |
| Other-relative | 79 | Private | 124744 | Some-college | 10 | Married-civ-spouse | Prof-specialty | White | Male | 0 | 0 | 20 | United-States | <=50K |
| Own-child | 23 | Private | 122272 | Bachelors | 13 | Never-married | Adm-clerical | White | Female | 0 | 0 | 30 | United-States | <=50K |
| Unmarried | 32 | Private | 186824 | HS-grad | 9 | Never-married | Machine-op-inspct | White | Male | 0 | 0 | 40 | United-States | <=50K |
| Wife | 28 | Private | 338409 | Bachelors | 13 | Married-civ-spouse | Prof-specialty | Black | Female | 0 | 0 | 40 | Cuba | <=50K |
.keys()로 키 값을 확인해보자.
grouped.groups.keys()
dict_keys(['Husband', 'Not-in-family', 'Other-relative', 'Own-child', 'Unmarried', 'Wife'])
이 값들은, .unique()의 값과 같다.
set(df['가족내역할'].unique()) == set(grouped.groups.keys())
True
즉, '가족내역할'의 값 그룹이 key 로 grouped 에 들어가있다.
그룹 바이로 그룹별로 평균, 합 등을 구할 수 있다.
가족 구성원의 나이 평균을 구해보자.
grouped['나이'].mean()
가족내역할
Husband 43.818616
Not-in-family 38.346057
Other-relative 33.164118
Own-child 24.827940
Unmarried 40.293964
Wife 39.846301
Name: 나이, dtype: float64
확인 결과, 남편들은 평균 43 세, 아내들은 평균 39 세 입니다.
그룹별 총 자본이득을 구해보자.
grouped['자본이득'].sum()
가족내역할
Husband 23682256
Not-in-family 6173333
Other-relative 274283
Own-child 788862
Unmarried 1568037
Wife 2602553
Name: 자본이득, dtype: int64
확인결과, 남편의 자본이득이 가장 높다.
.count()로 그룹별 출현 수를 얻을 수 있다. 이때 어느 열을 사용하든, 같은 결과를 얻을 수 있다. 임시로 나이의 count 를 구해보자.
grouped['나이'].count()
가족내역할
Husband 13193
Not-in-family 8305
Other-relative 981
Own-child 5068
Unmarried 3446
Wife 1568
Name: 나이, dtype: int64
이 자료에는 남편인 사람들이 가장 많다.
count 결과를 정렬해보자.
grouped['나이'].count().sort_values()
가족내역할
Other-relative 981
Wife 1568
Unmarried 3446
Own-child 5068
Not-in-family 8305
Husband 13193
Name: 나이, dtype: int64
내림차순으로 정렬해보자.
grouped['나이'].count().sort_values(ascending=False)
가족내역할
Husband 13193
Not-in-family 8305
Own-child 5068
Unmarried 3446
Wife 1568
Other-relative 981
Name: 나이, dtype: int64
.describe()로 자료의 요약을 살펴볼 수 있다.
grouped['나이'].describe()
| count | mean | std | min | 25% | 50% | 75% | max | |
|---|---|---|---|---|---|---|---|---|
| 가족내역할 | ||||||||
| Husband | 13193.0 | 43.818616 | 12.024349 | 17.0 | 35.0 | 43.0 | 52.0 | 90.0 |
| Not-in-family | 8305.0 | 38.346057 | 13.865379 | 17.0 | 27.0 | 35.0 | 47.0 | 90.0 |
| Other-relative | 981.0 | 33.164118 | 14.166235 | 17.0 | 22.0 | 28.0 | 41.0 | 90.0 |
| Own-child | 5068.0 | 24.827940 | 8.115799 | 17.0 | 19.0 | 22.0 | 27.0 | 90.0 |
| Unmarried | 3446.0 | 40.293964 | 11.605904 | 17.0 | 32.0 | 39.0 | 47.0 | 90.0 |
| Wife | 1568.0 | 39.846301 | 11.227700 | 18.0 | 31.0 | 39.0 | 47.0 | 90.0 |
하나의 그룹만을 선택하고 싶다면, .get_group()을 사용하면 된다.
grouped.get_group('Not-in-family').head()
| 가중치 | 결혼유무 | 고용형태 | 고향 | 교육년수 | 근무시간 | 나이 | 성별 | 수입 | 인종 | 자본손실 | 자본이득 | 직업명 | 최종학위 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 77516 | Never-married | State-gov | United-States | 13 | 40 | 39 | Male | <=50K | White | 0 | 2174 | Adm-clerical | Bachelors |
| 2 | 215646 | Divorced | Private | United-States | 9 | 40 | 38 | Male | <=50K | White | 0 | 0 | Handlers-cleaners | HS-grad |
| 6 | 160187 | Married-spouse-absent | Private | Jamaica | 5 | 16 | 49 | Female | <=50K | Black | 0 | 0 | Other-service | 9th |
| 8 | 45781 | Never-married | Private | United-States | 14 | 50 | 31 | Female | >50K | White | 0 | 14084 | Prof-specialty | Masters |
| 13 | 205019 | Never-married | Private | United-States | 12 | 50 | 32 | Male | <=50K | Black | 0 | 0 | Sales | Assoc-acdm |
그룹 바이 한 결과를, for문으로 돌릴 수 있다.
for name, group in grouped: # grouped 는 그룹별로 key 와 value 로 구성되어있는 사전
print(name) # key 값
print(group['근무시간'].sum()) # 주별 근무시간의 합
그룹마다 근무시간의 합이 출력된다.
groupby 를 사용해 그룹내에 그룹을 또 나눌 수 있다.
예제로 adult.csv 를 사용해보자.
import pandas as pd
df = pd.read_csv('adult.csv', skipinitialspace=True, header=None) # 값 앞에 공백이 포함 되어있기 때문에 제거
titles = {0: '나이', 1: '고용형태', 2: '가중치',
3: '최종학위', 4:'교육년수', 5: '결혼유무',
6: '직업명', 7:'가족내역할', 8:'인종',
9: '성별', 10: '자본이득', 11: '자본손실',
12: '근무시간', 13:'고향', 14:'수입'
}
df.rename(columns=titles, inplace=True)
df.head()
| 나이 | 고용형태 | 가중치 | 최종학위 | 교육년수 | 결혼유무 | 직업명 | 가족내역할 | 인종 | 성별 | 자본이득 | 자본손실 | 근무시간 | 고향 | 수입 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 39 | State-gov | 77516 | Bachelors | 13 | Never-married | Adm-clerical | Not-in-family | White | Male | 2174 | 0 | 40 | United-States | <=50K |
| 1 | 50 | Self-emp-not-inc | 83311 | Bachelors | 13 | Married-civ-spouse | Exec-managerial | Husband | White | Male | 0 | 0 | 13 | United-States | <=50K |
| 2 | 38 | Private | 215646 | HS-grad | 9 | Divorced | Handlers-cleaners | Not-in-family | White | Male | 0 | 0 | 40 | United-States | <=50K |
| 3 | 53 | Private | 234721 | 11th | 7 | Married-civ-spouse | Handlers-cleaners | Husband | Black | Male | 0 | 0 | 40 | United-States | <=50K |
| 4 | 28 | Private | 338409 | Bachelors | 13 | Married-civ-spouse | Prof-specialty | Wife | Black | Female | 0 | 0 | 40 | Cuba | <=50K |
이번에는 두개의 열을 .groupby() 에 리스트 형태로 넣어보자.
grouped = df.groupby(['가족내역할', '인종'])
grouped
<pandas.core.groupby.DataFrameGroupBy object at 0x000001D589EF7908>
그룹을 확인해보자
grouped.first()
| 나이 | 고용형태 | 가중치 | 최종학위 | 교육년수 | 결혼유무 | 직업명 | 성별 | 자본이득 | 자본손실 | 근무시간 | 고향 | 수입 | ||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 가족내역할 | 인종 | |||||||||||||
| Husband | Amer-Indian-Eskimo | 34 | Private | 245487 | 7th-8th | 4 | Married-civ-spouse | Transport-moving | Male | 0 | 0 | 45 | Mexico | <=50K |
| Asian-Pac-Islander | 30 | State-gov | 141297 | Bachelors | 13 | Married-civ-spouse | Prof-specialty | Male | 0 | 0 | 40 | India | >50K | |
| Black | 53 | Private | 234721 | 11th | 7 | Married-civ-spouse | Handlers-cleaners | Male | 0 | 0 | 40 | United-States | <=50K | |
| Other | 28 | Private | 166481 | 7th-8th | 4 | Married-civ-spouse | Handlers-cleaners | Male | 0 | 2179 | 40 | Puerto-Rico | <=50K | |
| White | 50 | Self-emp-not-inc | 83311 | Bachelors | 13 | Married-civ-spouse | Exec-managerial | Male | 0 | 0 | 13 | United-States | <=50K | |
| Not-in-family | Amer-Indian-Eskimo | 35 | Private | 153790 | Some-college | 10 | Never-married | Sales | Female | 0 | 0 | 40 | United-States | <=50K |
| Asian-Pac-Islander | 28 | Private | 88419 | HS-grad | 9 | Never-married | Exec-managerial | Female | 0 | 0 | 40 | England | <=50K | |
| Black | 49 | Private | 160187 | 9th | 5 | Married-spouse-absent | Other-service | Female | 0 | 0 | 16 | Jamaica | <=50K | |
| Other | 40 | Private | 237601 | Bachelors | 13 | Never-married | Sales | Female | 0 | 0 | 55 | United-States | >50K | |
| White | 39 | State-gov | 77516 | Bachelors | 13 | Never-married | Adm-clerical | Male | 2174 | 0 | 40 | United-States | <=50K | |
| Other-relative | Amer-Indian-Eskimo | 27 | Private | 23940 | HS-grad | 9 | Never-married | Handlers-cleaners | Male | 0 | 0 | 40 | United-States | <=50K |
| Asian-Pac-Islander | 33 | Private | 163003 | Bachelors | 13 | Never-married | Exec-managerial | Female | 0 | 0 | 40 | Philippines | <=50K | |
| Black | 42 | Private | 228456 | Bachelors | 13 | Separated | Other-service | Male | 0 | 0 | 50 | United-States | <=50K | |
| Other | 33 | Private | 110978 | Some-college | 10 | Divorced | Craft-repair | Female | 0 | 0 | 40 | United-States | <=50K | |
| White | 79 | Private | 124744 | Some-college | 10 | Married-civ-spouse | Prof-specialty | Male | 0 | 0 | 20 | United-States | <=50K | |
| Own-child | Amer-Indian-Eskimo | 20 | Private | 27337 | HS-grad | 9 | Never-married | Handlers-cleaners | Male | 0 | 0 | 48 | United-States | <=50K |
| Asian-Pac-Islander | 27 | Private | 116358 | Some-college | 10 | Never-married | Craft-repair | Male | 0 | 1980 | 40 | Philippines | <=50K | |
| Black | 20 | Private | 266015 | Some-college | 10 | Never-married | Sales | Male | 0 | 0 | 44 | United-States | <=50K | |
| Other | 23 | State-gov | 123983 | Bachelors | 13 | Never-married | Protective-serv | Male | 0 | 0 | 40 | United-States | <=50K | |
| White | 23 | Private | 122272 | Bachelors | 13 | Never-married | Adm-clerical | Female | 0 | 0 | 30 | United-States | <=50K | |
| Unmarried | Amer-Indian-Eskimo | 23 | Private | 99399 | Some-college | 10 | Never-married | Other-service | Female | 0 | 0 | 25 | United-States | <=50K |
| Asian-Pac-Islander | 44 | Self-emp-inc | 78374 | Masters | 14 | Divorced | Exec-managerial | Female | 0 | 0 | 40 | United-States | <=50K | |
| Black | 54 | Private | 302146 | HS-grad | 9 | Separated | Other-service | Female | 0 | 0 | 20 | United-States | <=50K | |
| Other | 65 | Private | 161400 | 11th | 7 | Widowed | Other-service | Male | 0 | 0 | 40 | United-States | <=50K | |
| White | 32 | Private | 186824 | HS-grad | 9 | Never-married | Machine-op-inspct | Male | 0 | 0 | 40 | United-States | <=50K | |
| Wife | Amer-Indian-Eskimo | 29 | Private | 100405 | 10th | 6 | Married-civ-spouse | Farming-fishing | Female | 0 | 0 | 40 | United-States | <=50K |
| Asian-Pac-Islander | 30 | Private | 117747 | HS-grad | 9 | Married-civ-spouse | Sales | Female | 0 | 1573 | 35 | Laos | <=50K | |
| Black | 28 | Private | 338409 | Bachelors | 13 | Married-civ-spouse | Prof-specialty | Female | 0 | 0 | 40 | Cuba | <=50K | |
| Other | 25 | Private | 32275 | Some-college | 10 | Married-civ-spouse | Exec-managerial | Female | 0 | 0 | 40 | United-States | <=50K | |
| White | 37 | Private | 284582 | Masters | 14 | Married-civ-spouse | Exec-managerial | Female | 0 | 0 | 40 | United-States | <=50K |
이번에는 가족내역할 안에서 인종으로 또 그룹이 나뉘었다.
그룹이 하나였을때와 마찬가지로, 자료의 평균, 합, 카운트 등을 얻을 수 있다.
가족내역할과 인종별로 평균 교육햇수를 구해보자.
grouped['교육년수'].mean()
가족내역할 인종
Husband Amer-Indian-Eskimo 9.250000
Asian-Pac-Islander 11.565854
Black 9.667660
Other 8.775000
White 10.339615
Not-in-family Amer-Indian-Eskimo 9.432099
Asian-Pac-Islander 11.046729
Black 9.653941
Other 9.534247
White 10.387088
Other-relative Amer-Indian-Eskimo 8.769231
Asian-Pac-Islander 9.914634
Black 8.902439
Other 8.750000
White 8.628242
Own-child Amer-Indian-Eskimo 9.145833
Asian-Pac-Islander 10.231214
Black 9.266667
Other 8.810811
White 9.494242
Unmarried Amer-Indian-Eskimo 9.224138
Asian-Pac-Islander 10.967033
Black 9.349805
Other 7.675676
White 9.723003
Wife Amer-Indian-Eskimo 10.157895
Asian-Pac-Islander 10.159420
Black 9.908497
Other 8.937500
White 10.566743
Name: 교육년수, dtype: float64
남편인데 인종이 에스키모일 경우 평균 교육기간은 9년이지만, 아내인데 인종이 에스키모일 경우 평균 교육기간은 10년이다.
이번에는 노동시간의 합을 구해보자.
grouped['근무시간'].sum()
가족내역할 인종
Husband Amer-Indian-Eskimo 3935
Asian-Pac-Islander 17835
Black 28156
Other 3449
White 528702
Not-in-family Amer-Indian-Eskimo 3352
Asian-Pac-Islander 8421
Black 32109
Other 2938
White 290327
Other-relative Amer-Indian-Eskimo 512
Asian-Pac-Islander 3014
Black 6007
Other 1053
White 25717
Own-child Amer-Indian-Eskimo 1662
Asian-Pac-Islander 6094
Black 18940
Other 1271
White 140642
Unmarried Amer-Indian-Eskimo 2234
Asian-Pac-Islander 3632
Black 29118
Other 1403
White 98362
Wife Amer-Indian-Eskimo 760
Asian-Pac-Islander 2696
Black 5703
Other 582
White 48058
Name: 근무시간, dtype: int64
값을 정렬해서 보고싶다면, .sort_values() 를 적용할 수 있다.
grouped['근무시간'].sum().sort_values()
가족내역할 인종
Other-relative Amer-Indian-Eskimo 512
Wife Other 582
Amer-Indian-Eskimo 760
Other-relative Other 1053
Own-child Other 1271
Unmarried Other 1403
Own-child Amer-Indian-Eskimo 1662
Unmarried Amer-Indian-Eskimo 2234
Wife Asian-Pac-Islander 2696
Not-in-family Other 2938
Other-relative Asian-Pac-Islander 3014
Not-in-family Amer-Indian-Eskimo 3352
Husband Other 3449
Unmarried Asian-Pac-Islander 3632
Husband Amer-Indian-Eskimo 3935
Wife Black 5703
Other-relative Black 6007
Own-child Asian-Pac-Islander 6094
Not-in-family Asian-Pac-Islander 8421
Husband Asian-Pac-Islander 17835
Own-child Black 18940
Other-relative White 25717
Husband Black 28156
Unmarried Black 29118
Not-in-family Black 32109
Wife White 48058
Unmarried White 98362
Own-child White 140642
Not-in-family White 290327
Husband White 528702
Name: 근무시간, dtype: int64
가족내역할과 상관없이 백인이 총 노동시간이 큰것으로 보여진다.
각 그룹마다 자료가 몇개있는지 보고싶다면 .count() 를 사용할 수 있다.
이때, 자료에 결측값이 없다면 어떤 열을 사용해도 같은 값을 얻는다.
grouped['고향'].count()
가족내역할 인종
Husband Amer-Indian-Eskimo 92
Asian-Pac-Islander 369
Black 647
Other 75
White 11764
Not-in-family Amer-Indian-Eskimo 81
Asian-Pac-Islander 202
Black 782
Other 66
White 7025
Other-relative Amer-Indian-Eskimo 13
Asian-Pac-Islander 73
Black 155
Other 27
White 684
Own-child Amer-Indian-Eskimo 48
Asian-Pac-Islander 166
Black 546
Other 35
White 4210
Unmarried Amer-Indian-Eskimo 58
Asian-Pac-Islander 86
Black 749
Other 36
White 2455
Wife Amer-Indian-Eskimo 19
Asian-Pac-Islander 60
Black 149
Other 14
White 1292
Name: 고향, dtype: int64
백인 남편에 대한 자료가 가장 많다.
지금까지는 그룹이 문자열로 표현된 경우만 살펴봤다. 이번에는 숫자 자료형을 groupby 해보자. 숫자 자료형을 groupby 하면, 각 숫자가 key 가 되어 그룹이 만들어진다.
예시로 문자열 자료형인 '성별'과 숫자 자료형인 '교육햇수'를 groupby 해보자.
grouped2 = df.groupby(['성별', '교육년수'])
grouped2.first()
| 나이 | 고용형태 | 가중치 | 최종학위 | 결혼유무 | 직업명 | 가족내역할 | 인종 | 자본이득 | 자본손실 | 근무시간 | 고향 | 수입 | ||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 성별 | 교육년수 | |||||||||||||
| Female | 1 | 53 | Local-gov | 140359 | Preschool | Never-married | Machine-op-inspct | Not-in-family | White | 0 | 0 | 35 | United-States | <=50K |
| 2 | 68 | Private | 38317 | 1st-4th | Divorced | Priv-house-serv | Not-in-family | White | 0 | 0 | 20 | United-States | <=50K | |
| 3 | 22 | Private | 399087 | 5th-6th | Married-civ-spouse | Machine-op-inspct | Other-relative | White | 0 | 0 | 40 | Mexico | <=50K | |
| 4 | 45 | Private | 433665 | 7th-8th | Separated | Other-service | Unmarried | White | 0 | 0 | 40 | Mexico | <=50K | |
| 5 | 49 | Private | 160187 | 9th | Married-spouse-absent | Other-service | Not-in-family | Black | 0 | 0 | 16 | Jamaica | <=50K | |
| 6 | 17 | Local-gov | 304873 | 10th | Never-married | Other-service | Own-child | White | 34095 | 0 | 32 | United-States | <=50K | |
| 7 | 18 | Private | 309634 | 11th | Never-married | Other-service | Own-child | White | 0 | 0 | 22 | United-States | <=50K | |
| 8 | 58 | Self-emp-not-inc | 211547 | 12th | Divorced | Sales | Not-in-family | White | 0 | 0 | 52 | United-States | <=50K | |
| 9 | 54 | Private | 302146 | HS-grad | Separated | Other-service | Unmarried | Black | 0 | 0 | 20 | United-States | <=50K | |
| 10 | 25 | Private | 32275 | Some-college | Married-civ-spouse | Exec-managerial | Wife | Other | 0 | 0 | 40 | United-States | <=50K | |
| 11 | 27 | Private | 163127 | Assoc-voc | Married-civ-spouse | Adm-clerical | Wife | White | 0 | 0 | 35 | United-States | <=50K | |
| 12 | 48 | Private | 171095 | Assoc-acdm | Divorced | Exec-managerial | Unmarried | White | 0 | 0 | 40 | England | <=50K | |
| 13 | 28 | Private | 338409 | Bachelors | Married-civ-spouse | Prof-specialty | Wife | Black | 0 | 0 | 40 | Cuba | <=50K | |
| 14 | 37 | Private | 284582 | Masters | Married-civ-spouse | Exec-managerial | Wife | White | 0 | 0 | 40 | United-States | <=50K | |
| 15 | 47 | Private | 51835 | Prof-school | Married-civ-spouse | Prof-specialty | Wife | White | 0 | 1902 | 60 | Honduras | >50K | |
| 16 | 43 | Federal-gov | 410867 | Doctorate | Never-married | Prof-specialty | Not-in-family | White | 0 | 0 | 50 | United-States | >50K | |
| Male | 1 | 51 | Local-gov | 241843 | Preschool | Married-civ-spouse | Other-service | Husband | White | 0 | 0 | 40 | United-States | <=50K |
| 2 | 64 | Private | 187656 | 1st-4th | Divorced | Machine-op-inspct | Not-in-family | White | 0 | 0 | 40 | United-States | <=50K | |
| 3 | 46 | Private | 216666 | 5th-6th | Married-civ-spouse | Machine-op-inspct | Husband | White | 0 | 0 | 40 | Mexico | <=50K | |
| 4 | 34 | Private | 245487 | 7th-8th | Married-civ-spouse | Transport-moving | Husband | Amer-Indian-Eskimo | 0 | 0 | 45 | Mexico | <=50K | |
| 5 | 35 | Federal-gov | 76845 | 9th | Married-civ-spouse | Farming-fishing | Husband | Black | 0 | 0 | 40 | United-States | <=50K | |
| 6 | 67 | Private | 212759 | 10th | Married-civ-spouse | Craft-repair | Husband | White | 0 | 0 | 2 | United-States | <=50K | |
| 7 | 53 | Private | 234721 | 11th | Married-civ-spouse | Handlers-cleaners | Husband | Black | 0 | 0 | 40 | United-States | <=50K | |
| 8 | 35 | Private | 92440 | 12th | Divorced | Craft-repair | Not-in-family | White | 0 | 0 | 50 | United-States | >50K | |
| 9 | 38 | Private | 215646 | HS-grad | Divorced | Handlers-cleaners | Not-in-family | White | 0 | 0 | 40 | United-States | <=50K | |
| 10 | 37 | Private | 280464 | Some-college | Married-civ-spouse | Exec-managerial | Husband | Black | 0 | 0 | 80 | United-States | >50K | |
| 11 | 40 | Private | 121772 | Assoc-voc | Married-civ-spouse | Craft-repair | Husband | Asian-Pac-Islander | 0 | 0 | 40 | United-States | >50K | |
| 12 | 32 | Private | 205019 | Assoc-acdm | Never-married | Sales | Not-in-family | Black | 0 | 0 | 50 | United-States | <=50K | |
| 13 | 39 | State-gov | 77516 | Bachelors | Never-married | Adm-clerical | Not-in-family | White | 2174 | 0 | 40 | United-States | <=50K | |
| 14 | 33 | Private | 202051 | Masters | Married-civ-spouse | Prof-specialty | Husband | White | 0 | 0 | 50 | United-States | <=50K | |
| 15 | 38 | Private | 65324 | Prof-school | Married-civ-spouse | Prof-specialty | Husband | White | 0 | 0 | 40 | United-States | >50K | |
| 16 | 40 | Private | 193524 | Doctorate | Married-civ-spouse | Prof-specialty | Husband | White | 0 | 0 | 60 | United-States | >50K |
교육햇수가 1부터 16까지 그룹지어졌다.
grouped2 의 평균 나이를 정렬해서 살펴보자.
grouped2['근무시간'].mean().sort_values()
성별 교육년수
Female 7 29.821759
8 31.791667
1 31.875000
2 31.978261
6 32.111864
5 33.916667
10 34.574840
3 36.047619
4 36.200000
Male 7 36.312248
Female 9 36.577286
12 37.358670
Male 8 37.768166
Female 11 37.830000
Male 1 38.828571
Female 13 39.329216
Male 6 39.336991
5 39.651351
3 39.859438
4 40.409465
2 40.622951
Female 14 41.113806
Male 10 41.528428
9 42.481367
12 42.554180
11 43.753968
13 44.037473
Female 15 44.793478
Male 14 45.065712
16 46.886850
Female 16 47.302326
Male 15 47.925620
Name: 근무시간, dtype: float64
교육햇수가 15, 16 일때, 성별 상관없이 평균 노동시간이 많은것을 알 수 있다.
지금까지는 자료를 Series 형태로 봤다.
[[]] 를 사용하면, 이를 데이터 프레임으로 바꿀 수 있다.
grouped2[['근무시간']].mean().head()
| 근무시간 | ||
|---|---|---|
| 성별 | 교육년수 | |
| Female | 1 | 31.875000 |
| 2 | 31.978261 | |
| 3 | 36.047619 | |
| 4 | 36.200000 | |
| 5 | 33.916667 |
여러 그룹으로 groupby 를 했지만, 한 그룹만 선택하고 싶다면, .groupby(level=0) 으로 접근할 수 있다.
예를 들어, 성별과 교육년수별로 근무시간 합을 구한후, 그 합들의 그룹별 평균을 구할 수 있다.
먼저 성별과 교육년수별 근무시간의 합을 구해보자.
grouped2['근무시간'].sum()
성별 교육년수
Female 1 510
2 1471
3 3028
4 5792
5 4884
6 9473
7 12883
8 4578
9 123997
10 97017
11 18915
12 15728
13 63674
14 22037
15 4121
16 4068
Male 1 1359
2 4956
3 9925
4 19639
5 14671
6 25097
7 26980
8 10915
9 302085
10 186255
11 38591
12 27490
13 164524
14 53493
15 23196
16 15332
Name: 근무시간, dtype: int64
다음으로는 총 근무시간을 성별 평균으로 보자.
뒤에 .groupby(level=0).mean() 를 붙이면 성별 평균을 볼 수 있다.
grouped2['근무시간'].sum().groupby(level=0).mean()
성별
Female 24511.00
Male 57781.75
Name: 근무시간, dtype: float64
만약 총 근무시간을 교육년수 평균으로 보고싶다면, level=1 라고 바꿔주면 된다.
grouped2['근무시간'].sum().groupby(level=1).mean()
교육년수
1 934.5
2 3213.5
3 6476.5
4 12715.5
5 9777.5
6 17285.0
7 19931.5
8 7746.5
9 213041.0
10 141636.0
11 28753.0
12 21609.0
13 114099.0
14 37765.0
15 13658.5
16 9700.0
Name: 근무시간, dtype: float64
.reset_index()로 다중 인덱스를 풀 수 있다.
grouped2['근무시간'].mean().reset_index().head()
| 성별 | 교육년수 | 근무시간 | |
|---|---|---|---|
| 0 | Female | 1 | 31.875000 |
| 1 | Female | 2 | 31.978261 |
| 2 | Female | 3 | 36.047619 |
| 3 | Female | 4 | 36.200000 |
| 4 | Female | 5 | 33.916667 |
.unstack()로 다중 인덱스를 가로와 세로축으로 나타낼 수 있다.
grouped2['근무시간'].mean().unstack()
| 교육년수 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 성별 | ||||||||||||||||
| Female | 31.875000 | 31.978261 | 36.047619 | 36.200000 | 33.916667 | 32.111864 | 29.821759 | 31.791667 | 36.577286 | 34.574840 | 37.830000 | 37.35867 | 39.329216 | 41.113806 | 44.793478 | 47.302326 |
| Male | 38.828571 | 40.622951 | 39.859438 | 40.409465 | 39.651351 | 39.336991 | 36.312248 | 37.768166 | 42.481367 | 41.528428 | 43.753968 | 42.55418 | 44.037473 | 45.065712 | 47.925620 | 46.886850 |
.apply() 와 .map() 등을 이용해 데이터 프레임의 열, 행, 값에 함수를 적용할 수 있다.
예제로 산불 데이터를 사용하자.
import pandas as pd
df = pd.read_excel('forestfires.xlsx')
df.head()
| X | Y | month | day | FFMC | DMC | DC | ISI | temp | RH | wind | rain | area | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 7 | 5 | mar | fri | 86.2 | 26.2 | 94.3 | 5.1 | 8.2 | 51 | 6.7 | 0.0 | 0.0 |
| 1 | 7 | 4 | oct | tue | 90.6 | 35.4 | 669.1 | 6.7 | 18.0 | 33 | 0.9 | 0.0 | 0.0 |
| 2 | 7 | 4 | oct | sat | 90.6 | 43.7 | 686.9 | 6.7 | 14.6 | 33 | 1.3 | 0.0 | 0.0 |
| 3 | 8 | 6 | mar | fri | 91.7 | 33.3 | 77.5 | 9.0 | 8.3 | 97 | 4.0 | 0.2 | 0.0 |
| 4 | 8 | 6 | mar | sun | 89.3 | 51.3 | 102.2 | 9.6 | 11.4 | 99 | 1.8 | 0.0 | 0.0 |
판다스에서 함수를 일괄 적용하는 방법에 크게 세가지 방식이 있다.
.map() 은 Series 에 적용할 수 있다.
먼저, ISI 열을 불러와 연습해보자.
s = df['ISI']
s.head()
0 5.1
1 6.7
2 6.7
3 9.0
4 9.6
Name: ISI, dtype: float64
s 는 Series 다.
이젠, 적용시킬 함수를 만들어보자.
값을 10으로 나누는 div_10 함수를 만들어보자.
def div_10(x):
return x / 10
잘 작동하는지 확인해보자.
div_10(30)
3.0
함수가 문제없이 실행된다.
다음으로는 함수를 .map() 안에 넘겨줘 s 에 적용해보자.
s.map(div_10).head()
0 0.51
1 0.67
2 0.67
3 0.90
4 0.96
Name: ISI, dtype: float64
div_10 함수가 적용된 값이 반환되었다.
같은 코드를 파이썬의 lambda 함수로 만들 수 있다.
람다 함수에 대한 자세한 설명은 별도 강의에서 찾아볼 수 있다.
x 마다 10 을 나누는 람다 함수를 .map() 에 넘겨준다.
s.map(lambda x: x / 10).head()
0 0.51
1 0.67
2 0.67
3 0.90
4 0.96
Name: ISI, dtype: float64
같은 결과를 얻을 수 있다.
이번에는 데이터 프레임에 적용할 수 있는 함수들을 살펴보자.
apply 는 각 값을 함수의 인수로 넘기는 것이 아니라, 열 혹은 행을 통으로 함수의 인수로 넘긴다.
apply 의 작동방식을 이해해보자. 먼저, 초기값인 axis=0 인 경우를 살펴보자.
axis=0 일때 데이터 프레임의 열 단위로 함수를 적용한다.
df.iteritems() 는 데이터 프레임의 열 이름과 열 값을 제공하는 generator 이다.
예시를 보이기 위해 for 문으로 첫 열의 이름과 열의 첫 다섯값만 프린트해보자.
for column_name, data in df.iteritems():
print(column_name)
print(data.head())
break
data 는 Series 형태임을 알 수 있다.
실제로 apply 에 넘겨지는 함수는 Series 인 data 를 인수로 받는다.
확인해보기 위해, 인수를 프린트 하는 함수 see_x 를 만들어 apply 에 넣어보자.
def see_x(x):
print(x.head())
간략하게 보기 위해 wind, rain, area 열에만 적용해보자.
df[['wind', 'rain', 'area']].apply(see_x)
wind None
rain None
area None
dtype: object
이 원리를 기반으로 apply 에 넣을 함수를 만들 수 있다.
이번에는 apply 에 see_x 함수 대신, Series 의 최대값에서 최소값을 빼는 max_min 함수를 적용해보자.
먼저 max_min 함수를 만든다.
.max()와 .min() 으로 최대, 최소값을 구한다.
def max_min(x):
return x.max() - x.min()
수치 자료인 wind, rain, area 에만 적용해보자.
df[['wind', 'rain', 'area']].apply(max_min)
wind 9.00
rain 6.40
area 1090.84
dtype: float64
max_min 함수가 데이터 프레임에 적용되었다.
이번에는 axis=1 일 경우를 살펴보자.
axis=1 일때 작동방식을 살펴보기 위해 .iterrows() 를 살펴보자.
.iterrows() 는 .iteritems()와 반대로 각 행의 위치와 행 정보를 generator 로 만들어준다.
for i, item in df.iterrows(): # 위치, 행 정보
print(i)
print(item)
break
첫 행의 행정보가 들어온것을 볼 수 있다.
위에서 만든 see_x 함수를 apply 에 적용해보자.
간략하게 보기 위해 첫 다섯 행만 살펴보자.
df.iloc[:5].apply(see_x, axis=1)
0 None
1 None
2 None
3 None
4 None
dtype: object
각 행이 함수의 인수로 넘겨진 것을 볼 수 있다.
이번에도 min_max 함수를 수치 자료형인 wind, rain, area 열에 적용해보자.
간략하게 보기 위해 첫 다섯 행에만 함수를 적용해보자.
df[['wind', 'rain', 'area']].iloc[:5].apply(max_min, axis=1)
0 6.7
1 0.9
2 1.3
3 4.0
4 1.8
dtype: float64
이번에는 행마다 wind, rain, area 중에서 최대값과 최소값의 차가 구해졌다.
applymap 은 데이터 프레임의 각 값에 함수를 적용한다.
예시로 wind, rain, area 에 '%' 표시를 붙여보자.
현재 wind, rain, area 에는 '%' 표시가 없다.
df[['wind', 'rain', 'area']].head()
| wind | rain | area | |
|---|---|---|---|
| 0 | 6.7 | 0.0 | 0.0 |
| 1 | 0.9 | 0.0 | 0.0 |
| 2 | 1.3 | 0.0 | 0.0 |
| 3 | 4.0 | 0.2 | 0.0 |
| 4 | 1.8 | 0.0 | 0.0 |
먼저 퍼센트 표시를 붙이는 함수 add_percent 를 만든다.
def add_percent(x):
return str(x) + '%'
함수가 잘 작동하는지 확인한다
add_percent(99)
'99%'
다음으로는 wind, rain, area 에 .applymap 을 사용해 add_percent 함수를 적용한다.
df[['wind', 'rain', 'area']].applymap(add_percent).head()
| wind | rain | area | |
|---|---|---|---|
| 0 | 6.7% | 0.0% | 0.0% |
| 1 | 0.9% | 0.0% | 0.0% |
| 2 | 1.3% | 0.0% | 0.0% |
| 3 | 4.0% | 0.2% | 0.0% |
| 4 | 1.8% | 0.0% | 0.0% |
원하는대로 자료가 바뀌었다.
같은 함수를 람다로 표현할 수 있다.
df[['wind', 'rain', 'area']].applymap(lambda x: str(x) + '%').head()
| wind | rain | area | |
|---|---|---|---|
| 0 | 6.7% | 0.0% | 0.0% |
| 1 | 0.9% | 0.0% | 0.0% |
| 2 | 1.3% | 0.0% | 0.0% |
| 3 | 4.0% | 0.2% | 0.0% |
| 4 | 1.8% | 0.0% | 0.0% |
agg 는 그룹 연산을 위한 메써드이다.
각 열에 적용하고 싶은 함수가 있을 때, agg 를 사용한다.
예시로 산불 데이터를 사용하자.
import pandas as pd
df = pd.read_excel('forestfires.xlsx')
df.head()
| X | Y | month | day | FFMC | DMC | DC | ISI | temp | RH | wind | rain | area | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 7 | 5 | mar | fri | 86.2 | 26.2 | 94.3 | 5.1 | 8.2 | 51 | 6.7 | 0.0 | 0.0 |
| 1 | 7 | 4 | oct | tue | 90.6 | 35.4 | 669.1 | 6.7 | 18.0 | 33 | 0.9 | 0.0 | 0.0 |
| 2 | 7 | 4 | oct | sat | 90.6 | 43.7 | 686.9 | 6.7 | 14.6 | 33 | 1.3 | 0.0 | 0.0 |
| 3 | 8 | 6 | mar | fri | 91.7 | 33.3 | 77.5 | 9.0 | 8.3 | 97 | 4.0 | 0.2 | 0.0 |
| 4 | 8 | 6 | mar | sun | 89.3 | 51.3 | 102.2 | 9.6 | 11.4 | 99 | 1.8 | 0.0 | 0.0 |
.agg() 에 max 함수를 넘겨, 각 열의 최대값을 구해보자.
df.agg(max)
X 9
Y 9
month sep
day wed
FFMC 96.2
DMC 291.3
DC 860.6
ISI 56.1
temp 33.3
RH 100
wind 9.4
rain 6.4
area 1090.84
dtype: object
열 별 최대값이 구해졌다.
month와 day 같은 경우, 값이 문자열일때는 알파벳 순서로 가장 뒤에 있는 값이 구해졌다.
문자열로 'max' 를 넘겨줘도 같은 결과를 구한다.
df.agg('max')
X 9
Y 9
month sep
day wed
FFMC 96.2
DMC 291.3
DC 860.6
ISI 56.1
temp 33.3
RH 100
wind 9.4
rain 6.4
area 1090.84
dtype: object
편의를 위해 연속변수들만 따로 모아 forest 라고 저장하자.
forest = df[['X', 'Y', 'temp', 'wind', 'rain', 'area']]
이번에는 sum, min, max 함수를 .agg() 에 리스트 형태로 넘겨보자.
forest.agg([sum, min, max])
| X | Y | temp | wind | rain | area | |
|---|---|---|---|---|---|---|
| sum | 2414 | 2223 | 9765.7 | 2077.1 | 11.2 | 6642.05 |
| min | 1 | 2 | 2.2 | 0.4 | 0.0 | 0.00 |
| max | 9 | 9 | 33.3 | 9.4 | 6.4 | 1090.84 |
세 함수가 모든 열에 적용되었다.
열마다 다른 함수를 적용하게 지정할 수 있다. 열마다 다른 함수를 지정하고 싶다면, 열 이름과 적용하고 싶은 함수를 짝지어 사전으로 넘겨준다.
최대 온도와 최소 강수량을 구하도록 사전을 만들어 넘겨보자.
forest.agg({'temp': max, 'rain': min})
temp 33.3
rain 0.0
dtype: float64
확인 결과 최대 온도는 33도이며, 최소 강수량은 0ml 이다.
사전에 리스트를 넘겨주면 하나의 열에만 여러 함수를 적용할 수도 있다.
온도는 최대, 최소 값을 모두 구하고 강수량은 최대값만 구해보자.
forest.agg({'temp':[min, max], 'rain':max})
| temp | rain | |
|---|---|---|
| max | 33.3 | 6.4 |
| min | 2.2 | NaN |
강수량은 최저값을 구하라고 사전에 지정하지 않았기 때문에 NaN 값이 나온다.
이번에는 내장 함수가 아닌 직접 생성한 함수를 적용시켜보자.
최대값에서 최소값을 빼는 max_min 함수를 적용해보자.
def max_min(x):
return max(x) - min(x)
forest.agg(max_min)
X 8.00
Y 7.00
temp 31.10
wind 9.00
rain 6.40
area 1090.84
dtype: float64
각 열마다 최대값에서 최소값을 뺀 결과가 나왔다.
위의 함수를 람다로 표현하면 다음과 같다.
forest.agg(lambda x: max(x) - min(x))
X 8.00
Y 7.00
temp 31.10
wind 9.00
rain 6.40
area 1090.84
dtype: float64
열마다 가장 많이 등장하는 값과 빈도를 구해주는 max_count 함수를 만들어보자.
def max_count(x):
max_index = x.value_counts().idxmax() # 가장 자주 등장하는 값
max_value = x.value_counts().max() # 등장 빈도
return max_index, max_value
forest.agg(max_count)
X (4.0, 91)
Y (4.0, 203)
temp (17.4, 8)
wind (2.2, 53)
rain (0.0, 509)
area (0.0, 247)
dtype: object
X 에서 가장 많이 등장하는 값은 4이며 91번 등장했다는 것을 알 수 있다.
groupby 로 그룹을 만든 후, agg 를 사용할 수 있다.
예시로 X 값을 그룹으로 잡아보자.
grouped = forest.groupby('X')
이제 각 그룹당 최고값을 구하도록 .agg(max) 를 구해보자.
grouped.agg(max)
| Y | temp | wind | rain | area | |
|---|---|---|---|---|---|
| X | |||||
| 1 | 5 | 32.4 | 8.5 | 0.0 | 212.88 |
| 2 | 5 | 33.1 | 9.4 | 0.0 | 200.94 |
| 3 | 6 | 32.3 | 8.5 | 0.0 | 35.88 |
| 4 | 6 | 32.6 | 8.5 | 0.4 | 154.88 |
| 5 | 6 | 27.6 | 8.0 | 1.4 | 24.24 |
| 6 | 6 | 33.3 | 9.4 | 0.0 | 1090.84 |
| 7 | 6 | 27.3 | 9.4 | 6.4 | 278.53 |
| 8 | 8 | 31.0 | 8.9 | 0.8 | 746.28 |
| 9 | 9 | 30.2 | 4.5 | 0.0 | 105.66 |
X 좌표가 1 일때, 풍량이 8.5 인데 비해, X 좌표가 9 일때, 풍량이 4.5 인것을 보니, X 가 1 인 좌표는 최고 바람세기가 높은 곳임을 알 수 있다.
앞에서와 마찬가지로 사전으로 각 열마다 다른 함수를 적용할 수 있다.
온도는 최고값을, 강수량은 총 강수량과 최대강수량을 구해보자.
forest.groupby('X').agg({'temp':max, 'rain':[sum, max]})
| temp | rain | ||
|---|---|---|---|
| max | sum | max | |
| X | |||
| 1 | 32.4 | 0.0 | 0.0 |
| 2 | 33.1 | 0.0 | 0.0 |
| 3 | 32.3 | 0.0 | 0.0 |
| 4 | 32.6 | 0.4 | 0.4 |
| 5 | 27.6 | 1.4 | 1.4 |
| 6 | 33.3 | 0.0 | 0.0 |
| 7 | 27.3 | 8.4 | 6.4 |
| 8 | 31.0 | 1.0 | 0.8 |
| 9 | 30.2 | 0.0 | 0.0 |
X 좌표마다 최고온도, 강수량의 합과 최고값을 볼 수 있다.
엑셀에서 피봇 테이블을 판다스로 구현해보자.
피봇 테이블이란, 열과 행의 그룹들을 값으로 보는 방식을 의미한다.
df.pivot_table(index=행에 두고 싶은 열, columns=열에 두고 싶은 열, values=값에 두고 싶은 열)산불 데이터를 예시로 피봇 테이블에 대해 알아보자.
import pandas as pd
forest = pd.read_excel('forestfires.xlsx')
forest.head()
| X | Y | month | day | FFMC | DMC | DC | ISI | temp | RH | wind | rain | area | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 7 | 5 | mar | fri | 86.2 | 26.2 | 94.3 | 5.1 | 8.2 | 51 | 6.7 | 0.0 | 0.0 |
| 1 | 7 | 4 | oct | tue | 90.6 | 35.4 | 669.1 | 6.7 | 18.0 | 33 | 0.9 | 0.0 | 0.0 |
| 2 | 7 | 4 | oct | sat | 90.6 | 43.7 | 686.9 | 6.7 | 14.6 | 33 | 1.3 | 0.0 | 0.0 |
| 3 | 8 | 6 | mar | fri | 91.7 | 33.3 | 77.5 | 9.0 | 8.3 | 97 | 4.0 | 0.2 | 0.0 |
| 4 | 8 | 6 | mar | sun | 89.3 | 51.3 | 102.2 | 9.6 | 11.4 | 99 | 1.8 | 0.0 | 0.0 |
월별 요일별 온도를 피봇 테이블로 나타내보자.
행에는 월을, 열에는 요일을, 그리고 값에는 온도를 나타내자.
forest.pivot_table(index='month', columns='day', values='temp')
| day | fri | mon | sat | sun | thu | tue | wed |
|---|---|---|---|---|---|---|---|
| month | |||||||
| apr | 16.700000 | 10.900000 | 9.300000 | 14.900000 | 5.800000 | NaN | 15.200000 |
| aug | 21.238095 | 22.693333 | 20.503448 | 21.257500 | 21.407692 | 21.842857 | 23.228000 |
| dec | 2.200000 | 4.600000 | NaN | 4.800000 | 5.100000 | 5.100000 | 5.100000 |
| feb | 11.680000 | 8.900000 | 11.725000 | 8.875000 | 6.700000 | 4.850000 | 8.800000 |
| jan | NaN | NaN | 5.300000 | 5.200000 | NaN | NaN | NaN |
| jul | 17.033333 | 21.575000 | 22.450000 | 24.360000 | 29.200000 | 22.500000 | 15.366667 |
| jun | 20.533333 | 18.033333 | 17.550000 | 18.225000 | 24.550000 | NaN | 25.200000 |
| mar | 14.000000 | 11.783333 | 15.430000 | 10.628571 | 11.420000 | 15.100000 | 12.450000 |
| may | 18.000000 | NaN | 11.300000 | NaN | NaN | NaN | NaN |
| nov | NaN | NaN | NaN | NaN | NaN | 11.800000 | NaN |
| oct | 11.300000 | 17.175000 | 16.933333 | 16.600000 | NaN | 19.850000 | 18.050000 |
| sep | 18.586842 | 18.125000 | 21.524000 | 20.437037 | 20.390476 | 18.721053 | 20.407143 |
피봇 테이블의 기본적으로 평균값을 구해준다.
결측값이 있어 NaN 이 자주 보인다. fill_value=0 이라는 옵션을 넘겨 NaN 을 0 으로 바꿔보자.
forest.pivot_table(index='month', columns='day', values='temp', fill_value=0)
| day | fri | mon | sat | sun | thu | tue | wed |
|---|---|---|---|---|---|---|---|
| month | |||||||
| apr | 16.700000 | 10.900000 | 9.300000 | 14.900000 | 5.800000 | 0.000000 | 15.200000 |
| aug | 21.238095 | 22.693333 | 20.503448 | 21.257500 | 21.407692 | 21.842857 | 23.228000 |
| dec | 2.200000 | 4.600000 | 0.000000 | 4.800000 | 5.100000 | 5.100000 | 5.100000 |
| feb | 11.680000 | 8.900000 | 11.725000 | 8.875000 | 6.700000 | 4.850000 | 8.800000 |
| jan | 0.000000 | 0.000000 | 5.300000 | 5.200000 | 0.000000 | 0.000000 | 0.000000 |
| jul | 17.033333 | 21.575000 | 22.450000 | 24.360000 | 29.200000 | 22.500000 | 15.366667 |
| jun | 20.533333 | 18.033333 | 17.550000 | 18.225000 | 24.550000 | 0.000000 | 25.200000 |
| mar | 14.000000 | 11.783333 | 15.430000 | 10.628571 | 11.420000 | 15.100000 | 12.450000 |
| may | 18.000000 | 0.000000 | 11.300000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 |
| nov | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 11.800000 | 0.000000 |
| oct | 11.300000 | 17.175000 | 16.933333 | 16.600000 | 0.000000 | 19.850000 | 18.050000 |
| sep | 18.586842 | 18.125000 | 21.524000 | 20.437037 | 20.390476 | 18.721053 | 20.407143 |
결측값이 모두 0 으로 처리되었다.
평균값이 아닌, 합을 구하고 싶다면 aggfunc=sum 이라는 옵션을 넘겨줄 수 있다.
forest.pivot_table(index='month', columns='day', values='temp', fill_value=0, aggfunc=sum)
| day | fri | mon | sat | sun | thu | tue | wed |
|---|---|---|---|---|---|---|---|
| month | |||||||
| apr | 16.7 | 10.9 | 9.3 | 44.7 | 11.6 | 0.0 | 15.2 |
| aug | 446.0 | 340.4 | 594.6 | 850.3 | 556.6 | 611.6 | 580.7 |
| dec | 2.2 | 18.4 | 0.0 | 4.8 | 5.1 | 5.1 | 5.1 |
| feb | 58.4 | 26.7 | 46.9 | 35.5 | 6.7 | 9.7 | 8.8 |
| jan | 0.0 | 0.0 | 5.3 | 5.2 | 0.0 | 0.0 | 0.0 |
| jul | 51.1 | 86.3 | 179.6 | 121.8 | 87.6 | 135.0 | 46.1 |
| jun | 61.6 | 54.1 | 35.1 | 72.9 | 49.1 | 0.0 | 75.6 |
| mar | 154.0 | 141.4 | 154.3 | 74.4 | 57.1 | 75.5 | 49.8 |
| may | 18.0 | 0.0 | 11.3 | 0.0 | 0.0 | 0.0 | 0.0 |
| nov | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 11.8 | 0.0 |
| oct | 11.3 | 68.7 | 50.8 | 49.8 | 0.0 | 39.7 | 36.1 |
| sep | 706.3 | 507.5 | 538.1 | 551.8 | 428.2 | 355.7 | 285.7 |
마찬가지로 최소값을 구하고 싶다면 aggfunc=min 이라는 옵션을 넘겨줄 수 있다.
forest.pivot_table(index='month', columns='day', values='temp', fill_value=0, aggfunc=min)
| day | fri | mon | sat | sun | thu | tue | wed |
|---|---|---|---|---|---|---|---|
| month | |||||||
| apr | 16.7 | 10.9 | 9.3 | 13.4 | 5.8 | 0.0 | 15.2 |
| aug | 11.2 | 8.0 | 5.1 | 10.4 | 16.2 | 14.4 | 16.6 |
| dec | 2.2 | 4.6 | 0.0 | 4.8 | 5.1 | 5.1 | 5.1 |
| feb | 7.5 | 5.3 | 4.6 | 4.2 | 6.7 | 4.6 | 8.8 |
| jan | 0.0 | 0.0 | 5.3 | 5.2 | 0.0 | 0.0 | 0.0 |
| jul | 13.4 | 17.9 | 16.6 | 18.2 | 27.2 | 17.1 | 12.6 |
| jun | 19.2 | 14.3 | 10.6 | 14.3 | 22.7 | 0.0 | 19.6 |
| mar | 8.2 | 8.3 | 11.6 | 5.5 | 5.3 | 14.1 | 8.9 |
| may | 18.0 | 0.0 | 11.3 | 0.0 | 0.0 | 0.0 | 0.0 |
| nov | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 11.8 | 0.0 |
| oct | 11.3 | 16.1 | 14.6 | 13.8 | 0.0 | 18.0 | 15.9 |
| sep | 10.1 | 9.8 | 15.4 | 12.2 | 12.9 | 13.1 | 10.5 |
만들어 놓은 피봇 테이블에 X 좌표도 추가해서 표현할 수 있다.
행에 'month' 를 ['month', 'day'] 로 바꾸고 columns='X' 라고 바꿔보자.
forest.pivot_table(index=['month', 'day'], columns='X', values='temp', fill_value=0)
| X | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | |
|---|---|---|---|---|---|---|---|---|---|---|
| month | day | |||||||||
| apr | fri | 0.000000 | 0.000000 | 0.000000 | 16.700000 | 0.00 | 0.000000 | 0.000000 | 0.000000 | 0.0 |
| mon | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.00 | 10.900000 | 0.000000 | 0.000000 | 0.0 | |
| sat | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.00 | 9.300000 | 0.000000 | 0.000000 | 0.0 | |
| sun | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 17.60 | 13.700000 | 13.400000 | 0.000000 | 0.0 | |
| thu | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.00 | 5.800000 | 0.000000 | 0.000000 | 0.0 | |
| wed | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.00 | 15.200000 | 0.000000 | 0.000000 | 0.0 | |
| aug | fri | 21.857143 | 23.600000 | 11.200000 | 21.800000 | 21.10 | 19.450000 | 23.300000 | 21.000000 | 25.0 |
| mon | 0.000000 | 27.475000 | 13.700000 | 32.600000 | 0.00 | 23.900000 | 17.700000 | 21.483333 | 0.0 | |
| sat | 21.860000 | 21.825000 | 19.100000 | 19.900000 | 0.00 | 22.750000 | 18.983333 | 20.075000 | 0.0 | |
| sun | 18.700000 | 22.430000 | 21.000000 | 20.275000 | 22.85 | 19.100000 | 19.200000 | 22.257143 | 0.0 | |
| thu | 24.366667 | 20.150000 | 18.200000 | 22.466667 | 20.30 | 22.200000 | 21.400000 | 21.666667 | 20.5 | |
| tue | 20.100000 | 22.537500 | 22.566667 | 19.580000 | 22.60 | 26.350000 | 24.450000 | 18.700000 | 0.0 | |
| wed | 19.933333 | 23.900000 | 21.700000 | 23.733333 | 0.00 | 20.700000 | 0.000000 | 25.216667 | 0.0 | |
| dec | fri | 0.000000 | 0.000000 | 0.000000 | 2.200000 | 0.00 | 0.000000 | 0.000000 | 0.000000 | 0.0 |
| mon | 0.000000 | 0.000000 | 4.600000 | 4.600000 | 0.00 | 0.000000 | 0.000000 | 0.000000 | 0.0 | |
| sun | 0.000000 | 0.000000 | 0.000000 | 4.800000 | 0.00 | 0.000000 | 0.000000 | 0.000000 | 0.0 | |
| thu | 0.000000 | 0.000000 | 0.000000 | 5.100000 | 0.00 | 0.000000 | 0.000000 | 0.000000 | 0.0 | |
| tue | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.00 | 5.100000 | 0.000000 | 0.000000 | 0.0 | |
| wed | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.00 | 0.000000 | 0.000000 | 5.100000 | 0.0 | |
| feb | fri | 0.000000 | 12.300000 | 0.000000 | 0.000000 | 7.50 | 14.700000 | 8.200000 | 0.000000 | 15.7 |
| mon | 0.000000 | 13.900000 | 0.000000 | 0.000000 | 0.00 | 5.300000 | 7.500000 | 0.000000 | 0.0 | |
| sat | 0.000000 | 4.600000 | 12.700000 | 14.800000 | 0.00 | 0.000000 | 0.000000 | 0.000000 | 0.0 | |
| sun | 0.000000 | 0.000000 | 0.000000 | 10.100000 | 12.40 | 4.200000 | 8.800000 | 0.000000 | 0.0 | |
| thu | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.00 | 0.000000 | 0.000000 | 0.000000 | 6.7 | |
| tue | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.00 | 4.850000 | 0.000000 | 0.000000 | 0.0 | |
| wed | 0.000000 | 0.000000 | 8.800000 | 0.000000 | 0.00 | 0.000000 | 0.000000 | 0.000000 | 0.0 | |
| jan | sat | 0.000000 | 5.300000 | 0.000000 | 0.000000 | 0.00 | 0.000000 | 0.000000 | 0.000000 | 0.0 |
| sun | 0.000000 | 0.000000 | 0.000000 | 5.200000 | 0.00 | 0.000000 | 0.000000 | 0.000000 | 0.0 | |
| jul | fri | 14.800000 | 13.400000 | 0.000000 | 22.900000 | 0.00 | 0.000000 | 0.000000 | 0.000000 | 0.0 |
| mon | 0.000000 | 0.000000 | 17.900000 | 0.000000 | 0.00 | 23.000000 | 22.600000 | 0.000000 | 22.8 | |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | |
| wed | 0.000000 | 0.000000 | 14.200000 | 0.000000 | 19.30 | 0.000000 | 12.600000 | 0.000000 | 0.0 | |
| jun | fri | 0.000000 | 0.000000 | 19.200000 | 0.000000 | 0.00 | 23.200000 | 0.000000 | 0.000000 | 0.0 |
| mon | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.00 | 19.900000 | 0.000000 | 14.300000 | 0.0 | |
| sat | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.00 | 10.600000 | 0.000000 | 0.000000 | 24.5 | |
| sun | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.00 | 14.300000 | 21.600000 | 15.400000 | 0.0 | |
| thu | 0.000000 | 22.700000 | 0.000000 | 26.400000 | 0.00 | 0.000000 | 0.000000 | 0.000000 | 0.0 | |
| wed | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.00 | 0.000000 | 0.000000 | 19.600000 | 28.0 | |
| mar | fri | 0.000000 | 0.000000 | 18.800000 | 14.850000 | 15.60 | 13.150000 | 8.200000 | 12.850000 | 0.0 |
| mon | 8.300000 | 0.000000 | 9.800000 | 11.950000 | 13.20 | 12.700000 | 13.550000 | 0.000000 | 0.0 | |
| sat | 0.000000 | 0.000000 | 14.733333 | 17.000000 | 15.10 | 15.300000 | 0.000000 | 0.000000 | 0.0 | |
| sun | 0.000000 | 8.500000 | 0.000000 | 10.600000 | 0.00 | 12.400000 | 11.500000 | 11.450000 | 0.0 | |
| thu | 0.000000 | 0.000000 | 0.000000 | 18.200000 | 11.60 | 9.100000 | 0.000000 | 0.000000 | 0.0 | |
| tue | 0.000000 | 15.200000 | 15.400000 | 14.100000 | 0.00 | 0.000000 | 0.000000 | 0.000000 | 0.0 | |
| wed | 0.000000 | 0.000000 | 10.050000 | 0.000000 | 0.00 | 14.850000 | 0.000000 | 0.000000 | 0.0 | |
| may | fri | 0.000000 | 0.000000 | 0.000000 | 18.000000 | 0.00 | 0.000000 | 0.000000 | 0.000000 | 0.0 |
| sat | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.00 | 11.300000 | 0.000000 | 0.000000 | 0.0 | |
| nov | tue | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.00 | 11.800000 | 0.000000 | 0.000000 | 0.0 |
| oct | fri | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.00 | 0.000000 | 11.300000 | 0.000000 | 0.0 |
| mon | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.00 | 0.000000 | 16.450000 | 17.900000 | 0.0 | |
| sat | 0.000000 | 0.000000 | 0.000000 | 18.400000 | 0.00 | 0.000000 | 16.200000 | 0.000000 | 0.0 | |
| sun | 0.000000 | 15.400000 | 20.600000 | 13.800000 | 0.00 | 0.000000 | 0.000000 | 0.000000 | 0.0 | |
| tue | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.00 | 21.700000 | 18.000000 | 0.000000 | 0.0 | |
| wed | 0.000000 | 0.000000 | 15.900000 | 0.000000 | 0.00 | 0.000000 | 0.000000 | 20.200000 | 0.0 | |
| sep | fri | 20.100000 | 19.600000 | 18.250000 | 18.650000 | 16.15 | 19.100000 | 18.914286 | 20.700000 | 0.0 |
| mon | 18.200000 | 19.833333 | 17.250000 | 14.966667 | 16.90 | 18.250000 | 19.350000 | 18.100000 | 0.0 | |
| sat | 25.366667 | 23.633333 | 24.200000 | 19.640000 | 24.10 | 20.771429 | 20.350000 | 17.800000 | 0.0 | |
| sun | 22.066667 | 17.700000 | 21.380000 | 20.016667 | 0.00 | 21.766667 | 18.733333 | 17.800000 | 0.0 | |
| thu | 21.525000 | 0.000000 | 17.850000 | 21.071429 | 21.60 | 17.750000 | 19.400000 | 23.450000 | 0.0 | |
| tue | 20.700000 | 17.600000 | 15.900000 | 16.500000 | 0.00 | 19.180000 | 20.700000 | 18.650000 | 24.3 | |
| wed | 0.000000 | 20.050000 | 18.500000 | 20.540000 | 23.90 | 25.350000 | 12.950000 | 0.000000 | 0.0 |
64 rows × 9 columns
행이 멀티 인덱스로 나뉘어진것을 볼 수 있다.
열을 멀티 컬럼으로 나누고 싶다면, 리스트를 열에 넣을 수 있다.
한눈에 보기 편하게 index='X'라고 두고, columns=['month', 'day'] 라고 표현하자.
forest.pivot_table(index='X', columns=['month', 'day'], values='temp', fill_value=0)
| month | apr | aug | ... | oct | sep | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| day | fri | mon | sat | sun | thu | wed | fri | mon | sat | sun | ... | sun | tue | wed | fri | mon | sat | sun | thu | tue | wed |
| X | |||||||||||||||||||||
| 1 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 21.857143 | 0.000000 | 21.860000 | 18.700000 | ... | 0.0 | 0.0 | 0.0 | 20.100000 | 18.200000 | 25.366667 | 22.066667 | 21.525000 | 20.70 | 0.00 |
| 2 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 23.600000 | 27.475000 | 21.825000 | 22.430000 | ... | 15.4 | 0.0 | 0.0 | 19.600000 | 19.833333 | 23.633333 | 17.700000 | 0.000000 | 17.60 | 20.05 |
| 3 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 11.200000 | 13.700000 | 19.100000 | 21.000000 | ... | 20.6 | 0.0 | 15.9 | 18.250000 | 17.250000 | 24.200000 | 21.380000 | 17.850000 | 15.90 | 18.50 |
| 4 | 16.7 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 21.800000 | 32.600000 | 19.900000 | 20.275000 | ... | 13.8 | 0.0 | 0.0 | 18.650000 | 14.966667 | 19.640000 | 20.016667 | 21.071429 | 16.50 | 20.54 |
| 5 | 0.0 | 0.0 | 0.0 | 17.6 | 0.0 | 0.0 | 21.100000 | 0.000000 | 0.000000 | 22.850000 | ... | 0.0 | 0.0 | 0.0 | 16.150000 | 16.900000 | 24.100000 | 0.000000 | 21.600000 | 0.00 | 23.90 |
| 6 | 0.0 | 10.9 | 9.3 | 13.7 | 5.8 | 15.2 | 19.450000 | 23.900000 | 22.750000 | 19.100000 | ... | 0.0 | 21.7 | 0.0 | 19.100000 | 18.250000 | 20.771429 | 21.766667 | 17.750000 | 19.18 | 25.35 |
| 7 | 0.0 | 0.0 | 0.0 | 13.4 | 0.0 | 0.0 | 23.300000 | 17.700000 | 18.983333 | 19.200000 | ... | 0.0 | 18.0 | 0.0 | 18.914286 | 19.350000 | 20.350000 | 18.733333 | 19.400000 | 20.70 | 12.95 |
| 8 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 21.000000 | 21.483333 | 20.075000 | 22.257143 | ... | 0.0 | 0.0 | 20.2 | 20.700000 | 18.100000 | 17.800000 | 17.800000 | 23.450000 | 18.65 | 0.00 |
| 9 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 25.000000 | 0.000000 | 0.000000 | 0.000000 | ... | 0.0 | 0.0 | 0.0 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 24.30 | 0.00 |
9 rows × 64 columns
여러 그룹을 데이터 프레임으로 나타내는 것을 다중 인덱스(multi index) 라고 부른다.
예시로 고객의 신상 정보와, 정기예금 가입여부를 모아놓은 데이터를 사용하자
import pandas as pd
bank = pd.read_csv('bank.csv', sep=';')
bank.head()
| age | job | marital | education | default | balance | housing | loan | contact | day | month | duration | campaign | pdays | previous | poutcome | y | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 30 | unemployed | married | primary | no | 1787 | no | no | cellular | 19 | oct | 79 | 1 | -1 | 0 | unknown | no |
| 1 | 33 | services | married | secondary | no | 4789 | yes | yes | cellular | 11 | may | 220 | 1 | 339 | 4 | failure | no |
| 2 | 35 | management | single | tertiary | no | 1350 | yes | no | cellular | 16 | apr | 185 | 1 | 330 | 1 | failure | no |
| 3 | 30 | management | married | tertiary | no | 1476 | yes | yes | unknown | 3 | jun | 199 | 4 | -1 | 0 | unknown | no |
| 4 | 59 | blue-collar | married | secondary | no | 0 | yes | no | unknown | 5 | may | 226 | 1 | -1 | 0 | unknown | no |
대표적으로, 피봇 테이블을 만들때 다중 인덱스가 만들어진다.
정기예금 가입 여부 별 결혼유무별 직업의 통장 잔고값을 피봇 테이블로 만들어보자.
pivot_df = bank.pivot_table(index=['y', 'marital'], columns='job', values='balance', fill_value=0)
pivot_df
| job | admin. | blue-collar | entrepreneur | housemaid | management | retired | self-employed | services | student | technician | unemployed | unknown | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| y | marital | ||||||||||||
| no | divorced | 854.431034 | 788.956522 | 1102.538462 | 405.444444 | 1313.990654 | 1622.814815 | 2921.600000 | 974.666667 | 0.000000 | 879.012346 | 842.909091 | 137.000000 |
| married | 1357.608511 | 1038.893617 | 1636.918699 | 1758.610390 | 1889.041667 | 2289.092857 | 1379.554622 | 1242.354545 | 519.222222 | 1437.311653 | 1048.075758 | 1201.208333 | |
| single | 1106.692913 | 1451.366667 | 2738.823529 | 3309.916667 | 1743.912351 | 3909.666667 | 896.882353 | 877.323810 | 1825.732143 | 1286.365957 | 1272.592593 | 3108.333333 | |
| yes | divorced | 1628.363636 | 440.100000 | -32.333333 | 2878.000000 | 2487.750000 | 2022.312500 | 1873.000000 | 640.625000 | 0.000000 | 509.375000 | 0.000000 | 0.000000 |
| married | 1320.354839 | 1543.142857 | 1283.555556 | 1768.857143 | 1665.090909 | 2752.388889 | 925.375000 | 1031.000000 | 10.000000 | 1433.476190 | 1744.000000 | 1574.833333 | |
| single | 1149.500000 | 517.375000 | 897.333333 | 10237.000000 | 1643.571429 | 1247.500000 | 2023.714286 | 1475.714286 | 1264.277778 | 1638.181818 | 418.250000 | 0.000000 |
행이 두 단계로 만들어진것을 볼 수 있다. 이를 다중 인덱스라고 부른다.
다중 인덱스 데이터 프레임에서 자료를 사용할때 데이터 프레임이 일렬인 상태로 놓일때 더욱 접근하기 쉽다.
.melt() 를 사용해 다중 인덱스를 풀어보자.
melted_df = pd.melt(pivot_df)
첫 12 자료를 살펴보자.
melted_df.iloc[:12]
| job | value | |
|---|---|---|
| 0 | admin. | 854.431034 |
| 1 | admin. | 1357.608511 |
| 2 | admin. | 1106.692913 |
| 3 | admin. | 1628.363636 |
| 4 | admin. | 1320.354839 |
| 5 | admin. | 1149.500000 |
| 6 | blue-collar | 788.956522 |
| 7 | blue-collar | 1038.893617 |
| 8 | blue-collar | 1451.366667 |
| 9 | blue-collar | 440.100000 |
| 10 | blue-collar | 1543.142857 |
| 11 | blue-collar | 517.375000 |
pivot_df 의 모든 열을 풀어, 하나의 긴 데이터 프레임으로 만들어졌다.
존재하는 데이터 프레임에 자료를 추가하는 것을 어팬드(append) 라고 부른다.
예제 데이터로 산불 자료를 사용하자.
import pandas as pd
df = pd.read_excel('forestfires.xlsx')
df.head()
| X | Y | month | day | FFMC | DMC | DC | ISI | temp | RH | wind | rain | area | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 7 | 5 | mar | fri | 86.2 | 26.2 | 94.3 | 5.1 | 8.2 | 51 | 6.7 | 0.0 | 0.0 |
| 1 | 7 | 4 | oct | tue | 90.6 | 35.4 | 669.1 | 6.7 | 18.0 | 33 | 0.9 | 0.0 | 0.0 |
| 2 | 7 | 4 | oct | sat | 90.6 | 43.7 | 686.9 | 6.7 | 14.6 | 33 | 1.3 | 0.0 | 0.0 |
| 3 | 8 | 6 | mar | fri | 91.7 | 33.3 | 77.5 | 9.0 | 8.3 | 97 | 4.0 | 0.2 | 0.0 |
| 4 | 8 | 6 | mar | sun | 89.3 | 51.3 | 102.2 | 9.6 | 11.4 | 99 | 1.8 | 0.0 | 0.0 |
연습을 위해 임의로 두 데이터 프레임 df1 과 df2 를 만들자.
df1 = df.iloc[:10] # 첫 열줄
df1.head()
| X | Y | month | day | FFMC | DMC | DC | ISI | temp | RH | wind | rain | area | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 7 | 5 | mar | fri | 86.2 | 26.2 | 94.3 | 5.1 | 8.2 | 51 | 6.7 | 0.0 | 0.0 |
| 1 | 7 | 4 | oct | tue | 90.6 | 35.4 | 669.1 | 6.7 | 18.0 | 33 | 0.9 | 0.0 | 0.0 |
| 2 | 7 | 4 | oct | sat | 90.6 | 43.7 | 686.9 | 6.7 | 14.6 | 33 | 1.3 | 0.0 | 0.0 |
| 3 | 8 | 6 | mar | fri | 91.7 | 33.3 | 77.5 | 9.0 | 8.3 | 97 | 4.0 | 0.2 | 0.0 |
| 4 | 8 | 6 | mar | sun | 89.3 | 51.3 | 102.2 | 9.6 | 11.4 | 99 | 1.8 | 0.0 | 0.0 |
df2 = df.iloc[-10:] # 마지막 열줄
df2.head()
| X | Y | month | day | FFMC | DMC | DC | ISI | temp | RH | wind | rain | area | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 507 | 2 | 4 | aug | fri | 91.0 | 166.9 | 752.6 | 7.1 | 25.9 | 41 | 3.6 | 0.0 | 0.00 |
| 508 | 1 | 2 | aug | fri | 91.0 | 166.9 | 752.6 | 7.1 | 25.9 | 41 | 3.6 | 0.0 | 0.00 |
| 509 | 5 | 4 | aug | fri | 91.0 | 166.9 | 752.6 | 7.1 | 21.1 | 71 | 7.6 | 1.4 | 2.17 |
| 510 | 6 | 5 | aug | fri | 91.0 | 166.9 | 752.6 | 7.1 | 18.2 | 62 | 5.4 | 0.0 | 0.43 |
| 511 | 8 | 6 | aug | sun | 81.6 | 56.7 | 665.6 | 1.9 | 27.8 | 35 | 2.7 | 0.0 | 0.00 |
df1 뒤에 df2 를 붙여보자.
df1.append(df2)
| X | Y | month | day | FFMC | DMC | DC | ISI | temp | RH | wind | rain | area | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 7 | 5 | mar | fri | 86.2 | 26.2 | 94.3 | 5.1 | 8.2 | 51 | 6.7 | 0.0 | 0.00 |
| 1 | 7 | 4 | oct | tue | 90.6 | 35.4 | 669.1 | 6.7 | 18.0 | 33 | 0.9 | 0.0 | 0.00 |
| 2 | 7 | 4 | oct | sat | 90.6 | 43.7 | 686.9 | 6.7 | 14.6 | 33 | 1.3 | 0.0 | 0.00 |
| 3 | 8 | 6 | mar | fri | 91.7 | 33.3 | 77.5 | 9.0 | 8.3 | 97 | 4.0 | 0.2 | 0.00 |
| 4 | 8 | 6 | mar | sun | 89.3 | 51.3 | 102.2 | 9.6 | 11.4 | 99 | 1.8 | 0.0 | 0.00 |
| 5 | 8 | 6 | aug | sun | 92.3 | 85.3 | 488.0 | 14.7 | 22.2 | 29 | 5.4 | 0.0 | 0.00 |
| 6 | 8 | 6 | aug | mon | 92.3 | 88.9 | 495.6 | 8.5 | 24.1 | 27 | 3.1 | 0.0 | 0.00 |
| 7 | 8 | 6 | aug | mon | 91.5 | 145.4 | 608.2 | 10.7 | 8.0 | 86 | 2.2 | 0.0 | 0.00 |
| 8 | 8 | 6 | sep | tue | 91.0 | 129.5 | 692.6 | 7.0 | 13.1 | 63 | 5.4 | 0.0 | 0.00 |
| 9 | 7 | 5 | sep | sat | 92.5 | 88.0 | 698.6 | 7.1 | 22.8 | 40 | 4.0 | 0.0 | 0.00 |
| 507 | 2 | 4 | aug | fri | 91.0 | 166.9 | 752.6 | 7.1 | 25.9 | 41 | 3.6 | 0.0 | 0.00 |
| 508 | 1 | 2 | aug | fri | 91.0 | 166.9 | 752.6 | 7.1 | 25.9 | 41 | 3.6 | 0.0 | 0.00 |
| 509 | 5 | 4 | aug | fri | 91.0 | 166.9 | 752.6 | 7.1 | 21.1 | 71 | 7.6 | 1.4 | 2.17 |
| 510 | 6 | 5 | aug | fri | 91.0 | 166.9 | 752.6 | 7.1 | 18.2 | 62 | 5.4 | 0.0 | 0.43 |
| 511 | 8 | 6 | aug | sun | 81.6 | 56.7 | 665.6 | 1.9 | 27.8 | 35 | 2.7 | 0.0 | 0.00 |
| 512 | 4 | 3 | aug | sun | 81.6 | 56.7 | 665.6 | 1.9 | 27.8 | 32 | 2.7 | 0.0 | 6.44 |
| 513 | 2 | 4 | aug | sun | 81.6 | 56.7 | 665.6 | 1.9 | 21.9 | 71 | 5.8 | 0.0 | 54.29 |
| 514 | 7 | 4 | aug | sun | 81.6 | 56.7 | 665.6 | 1.9 | 21.2 | 70 | 6.7 | 0.0 | 11.16 |
| 515 | 1 | 4 | aug | sat | 94.4 | 146.0 | 614.7 | 11.3 | 25.6 | 42 | 4.0 | 0.0 | 0.00 |
| 516 | 6 | 3 | nov | tue | 79.5 | 3.0 | 106.7 | 1.1 | 11.8 | 31 | 4.5 | 0.0 | 0.00 |
두 데이터 프레임이 합쳐졌다.
행 번호를 살펴보면, 9 다음의 숫자가 507 이다. 기존 인덱스를 무시하도록 ignore_index=True 라는 옵션을 넘겨보자.
df1.append(df2, ignore_index=True)
| X | Y | month | day | FFMC | DMC | DC | ISI | temp | RH | wind | rain | area | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 7 | 5 | mar | fri | 86.2 | 26.2 | 94.3 | 5.1 | 8.2 | 51 | 6.7 | 0.0 | 0.00 |
| 1 | 7 | 4 | oct | tue | 90.6 | 35.4 | 669.1 | 6.7 | 18.0 | 33 | 0.9 | 0.0 | 0.00 |
| 2 | 7 | 4 | oct | sat | 90.6 | 43.7 | 686.9 | 6.7 | 14.6 | 33 | 1.3 | 0.0 | 0.00 |
| 3 | 8 | 6 | mar | fri | 91.7 | 33.3 | 77.5 | 9.0 | 8.3 | 97 | 4.0 | 0.2 | 0.00 |
| 4 | 8 | 6 | mar | sun | 89.3 | 51.3 | 102.2 | 9.6 | 11.4 | 99 | 1.8 | 0.0 | 0.00 |
| 5 | 8 | 6 | aug | sun | 92.3 | 85.3 | 488.0 | 14.7 | 22.2 | 29 | 5.4 | 0.0 | 0.00 |
| 6 | 8 | 6 | aug | mon | 92.3 | 88.9 | 495.6 | 8.5 | 24.1 | 27 | 3.1 | 0.0 | 0.00 |
| 7 | 8 | 6 | aug | mon | 91.5 | 145.4 | 608.2 | 10.7 | 8.0 | 86 | 2.2 | 0.0 | 0.00 |
| 8 | 8 | 6 | sep | tue | 91.0 | 129.5 | 692.6 | 7.0 | 13.1 | 63 | 5.4 | 0.0 | 0.00 |
| 9 | 7 | 5 | sep | sat | 92.5 | 88.0 | 698.6 | 7.1 | 22.8 | 40 | 4.0 | 0.0 | 0.00 |
| 10 | 2 | 4 | aug | fri | 91.0 | 166.9 | 752.6 | 7.1 | 25.9 | 41 | 3.6 | 0.0 | 0.00 |
| 11 | 1 | 2 | aug | fri | 91.0 | 166.9 | 752.6 | 7.1 | 25.9 | 41 | 3.6 | 0.0 | 0.00 |
| 12 | 5 | 4 | aug | fri | 91.0 | 166.9 | 752.6 | 7.1 | 21.1 | 71 | 7.6 | 1.4 | 2.17 |
| 13 | 6 | 5 | aug | fri | 91.0 | 166.9 | 752.6 | 7.1 | 18.2 | 62 | 5.4 | 0.0 | 0.43 |
| 14 | 8 | 6 | aug | sun | 81.6 | 56.7 | 665.6 | 1.9 | 27.8 | 35 | 2.7 | 0.0 | 0.00 |
| 15 | 4 | 3 | aug | sun | 81.6 | 56.7 | 665.6 | 1.9 | 27.8 | 32 | 2.7 | 0.0 | 6.44 |
| 16 | 2 | 4 | aug | sun | 81.6 | 56.7 | 665.6 | 1.9 | 21.9 | 71 | 5.8 | 0.0 | 54.29 |
| 17 | 7 | 4 | aug | sun | 81.6 | 56.7 | 665.6 | 1.9 | 21.2 | 70 | 6.7 | 0.0 | 11.16 |
| 18 | 1 | 4 | aug | sat | 94.4 | 146.0 | 614.7 | 11.3 | 25.6 | 42 | 4.0 | 0.0 | 0.00 |
| 19 | 6 | 3 | nov | tue | 79.5 | 3.0 | 106.7 | 1.1 | 11.8 | 31 | 4.5 | 0.0 | 0.00 |
이번에는 행번호가 0에서 19까지 메겨졌다.
리스트를 사용해 한번에 여러 데이터 프레임을 어팬드할 수 있다.
df3 = df.iloc[10:20] # 10에서 20 번째 줄
df1.append([df2, df3])
| X | Y | month | day | FFMC | DMC | DC | ISI | temp | RH | wind | rain | area | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 7 | 5 | mar | fri | 86.2 | 26.2 | 94.3 | 5.1 | 8.2 | 51 | 6.7 | 0.0 | 0.00 |
| 1 | 7 | 4 | oct | tue | 90.6 | 35.4 | 669.1 | 6.7 | 18.0 | 33 | 0.9 | 0.0 | 0.00 |
| 2 | 7 | 4 | oct | sat | 90.6 | 43.7 | 686.9 | 6.7 | 14.6 | 33 | 1.3 | 0.0 | 0.00 |
| 3 | 8 | 6 | mar | fri | 91.7 | 33.3 | 77.5 | 9.0 | 8.3 | 97 | 4.0 | 0.2 | 0.00 |
| 4 | 8 | 6 | mar | sun | 89.3 | 51.3 | 102.2 | 9.6 | 11.4 | 99 | 1.8 | 0.0 | 0.00 |
| 5 | 8 | 6 | aug | sun | 92.3 | 85.3 | 488.0 | 14.7 | 22.2 | 29 | 5.4 | 0.0 | 0.00 |
| 6 | 8 | 6 | aug | mon | 92.3 | 88.9 | 495.6 | 8.5 | 24.1 | 27 | 3.1 | 0.0 | 0.00 |
| 7 | 8 | 6 | aug | mon | 91.5 | 145.4 | 608.2 | 10.7 | 8.0 | 86 | 2.2 | 0.0 | 0.00 |
| 8 | 8 | 6 | sep | tue | 91.0 | 129.5 | 692.6 | 7.0 | 13.1 | 63 | 5.4 | 0.0 | 0.00 |
| 9 | 7 | 5 | sep | sat | 92.5 | 88.0 | 698.6 | 7.1 | 22.8 | 40 | 4.0 | 0.0 | 0.00 |
| 507 | 2 | 4 | aug | fri | 91.0 | 166.9 | 752.6 | 7.1 | 25.9 | 41 | 3.6 | 0.0 | 0.00 |
| 508 | 1 | 2 | aug | fri | 91.0 | 166.9 | 752.6 | 7.1 | 25.9 | 41 | 3.6 | 0.0 | 0.00 |
| 509 | 5 | 4 | aug | fri | 91.0 | 166.9 | 752.6 | 7.1 | 21.1 | 71 | 7.6 | 1.4 | 2.17 |
| 510 | 6 | 5 | aug | fri | 91.0 | 166.9 | 752.6 | 7.1 | 18.2 | 62 | 5.4 | 0.0 | 0.43 |
| 511 | 8 | 6 | aug | sun | 81.6 | 56.7 | 665.6 | 1.9 | 27.8 | 35 | 2.7 | 0.0 | 0.00 |
| 512 | 4 | 3 | aug | sun | 81.6 | 56.7 | 665.6 | 1.9 | 27.8 | 32 | 2.7 | 0.0 | 6.44 |
| 513 | 2 | 4 | aug | sun | 81.6 | 56.7 | 665.6 | 1.9 | 21.9 | 71 | 5.8 | 0.0 | 54.29 |
| 514 | 7 | 4 | aug | sun | 81.6 | 56.7 | 665.6 | 1.9 | 21.2 | 70 | 6.7 | 0.0 | 11.16 |
| 515 | 1 | 4 | aug | sat | 94.4 | 146.0 | 614.7 | 11.3 | 25.6 | 42 | 4.0 | 0.0 | 0.00 |
| 516 | 6 | 3 | nov | tue | 79.5 | 3.0 | 106.7 | 1.1 | 11.8 | 31 | 4.5 | 0.0 | 0.00 |
| 10 | 7 | 5 | sep | sat | 92.5 | 88.0 | 698.6 | 7.1 | 17.8 | 51 | 7.2 | 0.0 | 0.00 |
| 11 | 7 | 5 | sep | sat | 92.8 | 73.2 | 713.0 | 22.6 | 19.3 | 38 | 4.0 | 0.0 | 0.00 |
| 12 | 6 | 5 | aug | fri | 63.5 | 70.8 | 665.3 | 0.8 | 17.0 | 72 | 6.7 | 0.0 | 0.00 |
| 13 | 6 | 5 | sep | mon | 90.9 | 126.5 | 686.5 | 7.0 | 21.3 | 42 | 2.2 | 0.0 | 0.00 |
| 14 | 6 | 5 | sep | wed | 92.9 | 133.3 | 699.6 | 9.2 | 26.4 | 21 | 4.5 | 0.0 | 0.00 |
| 15 | 6 | 5 | sep | fri | 93.3 | 141.2 | 713.9 | 13.9 | 22.9 | 44 | 5.4 | 0.0 | 0.00 |
| 16 | 5 | 5 | mar | sat | 91.7 | 35.8 | 80.8 | 7.8 | 15.1 | 27 | 5.4 | 0.0 | 0.00 |
| 17 | 8 | 5 | oct | mon | 84.9 | 32.8 | 664.2 | 3.0 | 16.7 | 47 | 4.9 | 0.0 | 0.00 |
| 18 | 6 | 4 | mar | wed | 89.2 | 27.9 | 70.8 | 6.3 | 15.9 | 35 | 4.0 | 0.0 | 0.00 |
| 19 | 6 | 4 | apr | sat | 86.3 | 27.4 | 97.1 | 5.1 | 9.3 | 44 | 4.5 | 0.0 | 0.00 |
이번에는 df1의 끝에 한 줄만 더 추가해보자.
one_row = df.iloc[-1] # 마지막 줄
one_row 는 시리즈임을 알 수 있다.
one_row
X 6
Y 3
month nov
day tue
FFMC 79.5
DMC 3
DC 106.7
ISI 1.1
temp 11.8
RH 31
wind 4.5
rain 0
area 0
Name: 516, dtype: object
데이터 프레임에 시리즈를 어팬드해보자.
df1.append(one_row)
| X | Y | month | day | FFMC | DMC | DC | ISI | temp | RH | wind | rain | area | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 7 | 5 | mar | fri | 86.2 | 26.2 | 94.3 | 5.1 | 8.2 | 51 | 6.7 | 0.0 | 0.0 |
| 1 | 7 | 4 | oct | tue | 90.6 | 35.4 | 669.1 | 6.7 | 18.0 | 33 | 0.9 | 0.0 | 0.0 |
| 2 | 7 | 4 | oct | sat | 90.6 | 43.7 | 686.9 | 6.7 | 14.6 | 33 | 1.3 | 0.0 | 0.0 |
| 3 | 8 | 6 | mar | fri | 91.7 | 33.3 | 77.5 | 9.0 | 8.3 | 97 | 4.0 | 0.2 | 0.0 |
| 4 | 8 | 6 | mar | sun | 89.3 | 51.3 | 102.2 | 9.6 | 11.4 | 99 | 1.8 | 0.0 | 0.0 |
| 5 | 8 | 6 | aug | sun | 92.3 | 85.3 | 488.0 | 14.7 | 22.2 | 29 | 5.4 | 0.0 | 0.0 |
| 6 | 8 | 6 | aug | mon | 92.3 | 88.9 | 495.6 | 8.5 | 24.1 | 27 | 3.1 | 0.0 | 0.0 |
| 7 | 8 | 6 | aug | mon | 91.5 | 145.4 | 608.2 | 10.7 | 8.0 | 86 | 2.2 | 0.0 | 0.0 |
| 8 | 8 | 6 | sep | tue | 91.0 | 129.5 | 692.6 | 7.0 | 13.1 | 63 | 5.4 | 0.0 | 0.0 |
| 9 | 7 | 5 | sep | sat | 92.5 | 88.0 | 698.6 | 7.1 | 22.8 | 40 | 4.0 | 0.0 | 0.0 |
| 516 | 6 | 3 | nov | tue | 79.5 | 3.0 | 106.7 | 1.1 | 11.8 | 31 | 4.5 | 0.0 | 0.0 |
자료 유형이 달라도 가능하다.
concatenate 방식으로 두 개 이상의 데이터 프레임을 합쳐보자.
pd.concat([df1, df2])산불 데이터를 예제로 사용하자.
import pandas as pd
df = pd.read_excel('forestfires.xlsx')
df.head()
| X | Y | month | day | FFMC | DMC | DC | ISI | temp | RH | wind | rain | area | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 7 | 5 | mar | fri | 86.2 | 26.2 | 94.3 | 5.1 | 8.2 | 51 | 6.7 | 0.0 | 0.0 |
| 1 | 7 | 4 | oct | tue | 90.6 | 35.4 | 669.1 | 6.7 | 18.0 | 33 | 0.9 | 0.0 | 0.0 |
| 2 | 7 | 4 | oct | sat | 90.6 | 43.7 | 686.9 | 6.7 | 14.6 | 33 | 1.3 | 0.0 | 0.0 |
| 3 | 8 | 6 | mar | fri | 91.7 | 33.3 | 77.5 | 9.0 | 8.3 | 97 | 4.0 | 0.2 | 0.0 |
| 4 | 8 | 6 | mar | sun | 89.3 | 51.3 | 102.2 | 9.6 | 11.4 | 99 | 1.8 | 0.0 | 0.0 |
임의로 데이터 프레임을 나눠보자.
xy = df[['X', 'Y']][:5] # X, Y 열의 첫 5 줄
xy
| X | Y | |
|---|---|---|
| 0 | 7 | 5 |
| 1 | 7 | 4 |
| 2 | 7 | 4 |
| 3 | 8 | 6 |
| 4 | 8 | 6 |
xy2 = df[['X', 'Y']][-5:] # X, Y 열의 마지막 5 줄
xy2
| X | Y | |
|---|---|---|
| 512 | 4 | 3 |
| 513 | 2 | 4 |
| 514 | 7 | 4 |
| 515 | 1 | 4 |
| 516 | 6 | 3 |
pd.concat()을 사용해 수직으로 xy 와 xy2를 연결시켜보자.
pd.concat([xy, xy2])
| X | Y | |
|---|---|---|
| 0 | 7 | 5 |
| 1 | 7 | 4 |
| 2 | 7 | 4 |
| 3 | 8 | 6 |
| 4 | 8 | 6 |
| 512 | 4 | 3 |
| 513 | 2 | 4 |
| 514 | 7 | 4 |
| 515 | 1 | 4 |
| 516 | 6 | 3 |
append 처럼 두 데이터 프레임이 합쳐졌다.
행이름을 무시하도록 ignore_index=True옵션을 넘겨보자.
pd.concat([xy, xy2], ignore_index=True)
| X | Y | |
|---|---|---|
| 0 | 7 | 5 |
| 1 | 7 | 4 |
| 2 | 7 | 4 |
| 3 | 8 | 6 |
| 4 | 8 | 6 |
| 5 | 4 | 3 |
| 6 | 2 | 4 |
| 7 | 7 | 4 |
| 8 | 1 | 4 |
| 9 | 6 | 3 |
행 이름이 다시 정리되었다.
append 에서는 수평으로 데이터를 합칠 수 없지만, concat 에서는 axis=1 옵션으로 가능하다.
예시를 보이기 위해 wind 의 첫 5줄을 wind라고 저장하자.
wind = df[['wind']][:5] # 첫 5 줄
wind
| wind | |
|---|---|
| 0 | 6.7 |
| 1 | 0.9 |
| 2 | 1.3 |
| 3 | 4.0 |
| 4 | 1.8 |
xy 와 wind를 수평으로 합쳐보자.
pd.concat([xy, wind])
| X | Y | wind | |
|---|---|---|---|
| 0 | 7.0 | 5.0 | NaN |
| 1 | 7.0 | 4.0 | NaN |
| 2 | 7.0 | 4.0 | NaN |
| 3 | 8.0 | 6.0 | NaN |
| 4 | 8.0 | 6.0 | NaN |
| 0 | NaN | NaN | 6.7 |
| 1 | NaN | NaN | 0.9 |
| 2 | NaN | NaN | 1.3 |
| 3 | NaN | NaN | 4.0 |
| 4 | NaN | NaN | 1.8 |
wind 라는 열이 xy에 존재하지 않기 때문에 판다스는 자동으로 wind 라는 열을 수평으로 붙였다.
하지만 wind 의 열의 값이 NaN으로 표현되고, 수직으로 자료가 붙었다. 이는 concat 의 초기값에 axis=0이 기본으로 들어있기 때문이다.
axis=1옵션을 넘겨보자.
pd.concat([xy, wind], axis=1)
| X | Y | wind | |
|---|---|---|---|
| 0 | 7 | 5 | 6.7 |
| 1 | 7 | 4 | 0.9 |
| 2 | 7 | 4 | 1.3 |
| 3 | 8 | 6 | 4.0 |
| 4 | 8 | 6 | 1.8 |
원하는대로 자료가 수평으로 합쳐졌다.
concat 메써드는 기본적으로 두 데이터 프레임의 합집합을 보여준다. 이는 초기값 중에 join='outer가 기본값으로 들어있기 때문이다.
만약 두 데이터 프레임의 교집합을 보고 싶다면, join='inner' 로 해결할 수 있다.
X 열이 두 데이터 프레임에서 겹치도록 wind2 를 만들어보자.
wind2 = df[['wind', 'X']][:5]
wind2
| wind | X | |
|---|---|---|
| 0 | 6.7 | 7 |
| 1 | 0.9 | 7 |
| 2 | 1.3 | 7 |
| 3 | 4.0 | 8 |
| 4 | 1.8 | 8 |
xy의 모양은 다음과 같다.
xy
| X | Y | |
|---|---|---|
| 0 | 7 | 5 |
| 1 | 7 | 4 |
| 2 | 7 | 4 |
| 3 | 8 | 6 |
| 4 | 8 | 6 |
이 둘을 concat 으로 합쳐보자.
pd.concat([xy, wind2])
| X | Y | wind | |
|---|---|---|---|
| 0 | 7 | 5.0 | NaN |
| 1 | 7 | 4.0 | NaN |
| 2 | 7 | 4.0 | NaN |
| 3 | 8 | 6.0 | NaN |
| 4 | 8 | 6.0 | NaN |
| 0 | 7 | NaN | 6.7 |
| 1 | 7 | NaN | 0.9 |
| 2 | 7 | NaN | 1.3 |
| 3 | 8 | NaN | 4.0 |
| 4 | 8 | NaN | 1.8 |
확인결과, wind 라는 열이 수평으로 생기기는 했지만, 수직으로 데이터 프레임이 합쳐졌다.
이번에는 join='inner'옵션을 넘겨보자.
'inner' 옵션은 데이터 프레임의 교집합만 나타낼것을 의미한다.
pd.concat([xy, wind2], join='inner')
| X | |
|---|---|
| 0 | 7 |
| 1 | 7 |
| 2 | 7 |
| 3 | 8 |
| 4 | 8 |
| 0 | 7 |
| 1 | 7 |
| 2 | 7 |
| 3 | 8 |
| 4 | 8 |
Y와 wind 는 새로운 데이터 프레임에서 빠졌다.
데이터 프레임 내에 있는 특정 값을, 원하는 값으로 변경할 수 있다.
값을 변경할 때는 판다스의 .replace() 메써드를 사용한다.
예시로 adult 데이터를 살펴보자.
import pandas as pd
df = pd.read_csv('adult.csv', header=None, skipinitialspace=True)
df.head()
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 39 | State-gov | 77516 | Bachelors | 13 | Never-married | Adm-clerical | Not-in-family | White | Male | 2174 | 0 | 40 | United-States | <=50K |
| 1 | 50 | Self-emp-not-inc | 83311 | Bachelors | 13 | Married-civ-spouse | Exec-managerial | Husband | White | Male | 0 | 0 | 13 | United-States | <=50K |
| 2 | 38 | Private | 215646 | HS-grad | 9 | Divorced | Handlers-cleaners | Not-in-family | White | Male | 0 | 0 | 40 | United-States | <=50K |
| 3 | 53 | Private | 234721 | 11th | 7 | Married-civ-spouse | Handlers-cleaners | Husband | Black | Male | 0 | 0 | 40 | United-States | <=50K |
| 4 | 28 | Private | 338409 | Bachelors | 13 | Married-civ-spouse | Prof-specialty | Wife | Black | Female | 0 | 0 | 40 | Cuba | <=50K |
9번 열의 Male, Female 값을 '남', '여' 값으로 바꿔보자.
.replace() 안에, 찾아야할 값과, 바꿀 값을 넘겨주면 된다.
df.replace('Male', '남').head()
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 39 | State-gov | 77516 | Bachelors | 13 | Never-married | Adm-clerical | Not-in-family | White | 남 | 2174 | 0 | 40 | United-States | <=50K |
| 1 | 50 | Self-emp-not-inc | 83311 | Bachelors | 13 | Married-civ-spouse | Exec-managerial | Husband | White | 남 | 0 | 0 | 13 | United-States | <=50K |
| 2 | 38 | Private | 215646 | HS-grad | 9 | Divorced | Handlers-cleaners | Not-in-family | White | 남 | 0 | 0 | 40 | United-States | <=50K |
| 3 | 53 | Private | 234721 | 11th | 7 | Married-civ-spouse | Handlers-cleaners | Husband | Black | 남 | 0 | 0 | 40 | United-States | <=50K |
| 4 | 28 | Private | 338409 | Bachelors | 13 | Married-civ-spouse | Prof-specialty | Wife | Black | Female | 0 | 0 | 40 | Cuba | <=50K |
9번 열의 값이 바뀌었다. 이번에는 Female 을 '여'로 값을 바꿔보자.
df.replace('Female', '여').head()
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 39 | State-gov | 77516 | Bachelors | 13 | Never-married | Adm-clerical | Not-in-family | White | Male | 2174 | 0 | 40 | United-States | <=50K |
| 1 | 50 | Self-emp-not-inc | 83311 | Bachelors | 13 | Married-civ-spouse | Exec-managerial | Husband | White | Male | 0 | 0 | 13 | United-States | <=50K |
| 2 | 38 | Private | 215646 | HS-grad | 9 | Divorced | Handlers-cleaners | Not-in-family | White | Male | 0 | 0 | 40 | United-States | <=50K |
| 3 | 53 | Private | 234721 | 11th | 7 | Married-civ-spouse | Handlers-cleaners | Husband | Black | Male | 0 | 0 | 40 | United-States | <=50K |
| 4 | 28 | Private | 338409 | Bachelors | 13 | Married-civ-spouse | Prof-specialty | Wife | Black | 여 | 0 | 0 | 40 | Cuba | <=50K |
만약 이를 df 에 저장하고 싶다면 inplace=True옵션을 넘겨야 한다.
df.replace('Female', '여', inplace=True)
df.head()
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 39 | State-gov | 77516 | Bachelors | 13 | Never-married | Adm-clerical | Not-in-family | White | Male | 2174 | 0 | 40 | United-States | <=50K |
| 1 | 50 | Self-emp-not-inc | 83311 | Bachelors | 13 | Married-civ-spouse | Exec-managerial | Husband | White | Male | 0 | 0 | 13 | United-States | <=50K |
| 2 | 38 | Private | 215646 | HS-grad | 9 | Divorced | Handlers-cleaners | Not-in-family | White | Male | 0 | 0 | 40 | United-States | <=50K |
| 3 | 53 | Private | 234721 | 11th | 7 | Married-civ-spouse | Handlers-cleaners | Husband | Black | Male | 0 | 0 | 40 | United-States | <=50K |
| 4 | 28 | Private | 338409 | Bachelors | 13 | Married-civ-spouse | Prof-specialty | Wife | Black | 여 | 0 | 0 | 40 | Cuba | <=50K |
사전을 이용해 한번에 이를 바꿀 수 있다.
df.replace({'Male': '남', 'Female': '여'}).head()
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 39 | State-gov | 77516 | Bachelors | 13 | Never-married | Adm-clerical | Not-in-family | White | 남 | 2174 | 0 | 40 | United-States | <=50K |
| 1 | 50 | Self-emp-not-inc | 83311 | Bachelors | 13 | Married-civ-spouse | Exec-managerial | Husband | White | 남 | 0 | 0 | 13 | United-States | <=50K |
| 2 | 38 | Private | 215646 | HS-grad | 9 | Divorced | Handlers-cleaners | Not-in-family | White | 남 | 0 | 0 | 40 | United-States | <=50K |
| 3 | 53 | Private | 234721 | 11th | 7 | Married-civ-spouse | Handlers-cleaners | Husband | Black | 남 | 0 | 0 | 40 | United-States | <=50K |
| 4 | 28 | Private | 338409 | Bachelors | 13 | Married-civ-spouse | Prof-specialty | Wife | Black | 여 | 0 | 0 | 40 | Cuba | <=50K |
리스트를 사용할수도 있다.
df.replace(['Male', 'Female'], ['남', '여']).head()
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 39 | State-gov | 77516 | Bachelors | 13 | Never-married | Adm-clerical | Not-in-family | White | 남 | 2174 | 0 | 40 | United-States | <=50K |
| 1 | 50 | Self-emp-not-inc | 83311 | Bachelors | 13 | Married-civ-spouse | Exec-managerial | Husband | White | 남 | 0 | 0 | 13 | United-States | <=50K |
| 2 | 38 | Private | 215646 | HS-grad | 9 | Divorced | Handlers-cleaners | Not-in-family | White | 남 | 0 | 0 | 40 | United-States | <=50K |
| 3 | 53 | Private | 234721 | 11th | 7 | Married-civ-spouse | Handlers-cleaners | Husband | Black | 남 | 0 | 0 | 40 | United-States | <=50K |
| 4 | 28 | Private | 338409 | Bachelors | 13 | Married-civ-spouse | Prof-specialty | Wife | Black | 여 | 0 | 0 | 40 | Cuba | <=50K |
regex=True 옵션을 넘기면 정규표현을 사용해 문자열을 바꿀수도 있다.
문자열에서 간혹 보이는 '-' 표시를 ' ' 표시로 바꿔보자.
df.head()
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 39 | State-gov | 77516 | Bachelors | 13 | Never-married | Adm-clerical | Not-in-family | White | Male | 2174 | 0 | 40 | United-States | <=50K |
| 1 | 50 | Self-emp-not-inc | 83311 | Bachelors | 13 | Married-civ-spouse | Exec-managerial | Husband | White | Male | 0 | 0 | 13 | United-States | <=50K |
| 2 | 38 | Private | 215646 | HS-grad | 9 | Divorced | Handlers-cleaners | Not-in-family | White | Male | 0 | 0 | 40 | United-States | <=50K |
| 3 | 53 | Private | 234721 | 11th | 7 | Married-civ-spouse | Handlers-cleaners | Husband | Black | Male | 0 | 0 | 40 | United-States | <=50K |
| 4 | 28 | Private | 338409 | Bachelors | 13 | Married-civ-spouse | Prof-specialty | Wife | Black | 여 | 0 | 0 | 40 | Cuba | <=50K |
df.replace(r'-', ' ', regex=True).head()
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 39 | State gov | 77516 | Bachelors | 13 | Never married | Adm clerical | Not in family | White | Male | 2174 | 0 | 40 | United States | <=50K |
| 1 | 50 | Self emp not inc | 83311 | Bachelors | 13 | Married civ spouse | Exec managerial | Husband | White | Male | 0 | 0 | 13 | United States | <=50K |
| 2 | 38 | Private | 215646 | HS grad | 9 | Divorced | Handlers cleaners | Not in family | White | Male | 0 | 0 | 40 | United States | <=50K |
| 3 | 53 | Private | 234721 | 11th | 7 | Married civ spouse | Handlers cleaners | Husband | Black | Male | 0 | 0 | 40 | United States | <=50K |
| 4 | 28 | Private | 338409 | Bachelors | 13 | Married civ spouse | Prof specialty | Wife | Black | 여 | 0 | 0 | 40 | Cuba | <=50K |
하이픈이 모두 공백으로 바뀌었다.
UCI 기계학습 저장소에서 와인 퀄리티 데이터셋를 사용한다.
Python 코드를 이용해 다운로드 받으려면 다음과 같이 한다.
먼저 판다스를 임포트한다.
import pandas as pd
pandas의 read_csv 함수를 이용하여 데이터를 읽어들인다. read_csv 함수는 파일은 물론 인터넷 주소에서도 데이터를 읽어올 수 있다. sep=';'은 이 파일이 세미콜론(;)으로 구분되어 있기 때문에 지정해주는 것이다.
red_wine = pd.read_csv(
'https://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-red.csv',
sep=';')
white_wine = pd.read_csv(
'https://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-white.csv',
sep=';')
읽어온 파일의 앞부분을 확인해보자.
red_wine.head()
| fixed acidity | volatile acidity | citric acid | residual sugar | chlorides | free sulfur dioxide | total sulfur dioxide | density | pH | sulphates | alcohol | quality | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 7.4 | 0.70 | 0.00 | 1.9 | 0.076 | 11.0 | 34.0 | 0.9978 | 3.51 | 0.56 | 9.4 | 5 |
| 1 | 7.8 | 0.88 | 0.00 | 2.6 | 0.098 | 25.0 | 67.0 | 0.9968 | 3.20 | 0.68 | 9.8 | 5 |
| 2 | 7.8 | 0.76 | 0.04 | 2.3 | 0.092 | 15.0 | 54.0 | 0.9970 | 3.26 | 0.65 | 9.8 | 5 |
| 3 | 11.2 | 0.28 | 0.56 | 1.9 | 0.075 | 17.0 | 60.0 | 0.9980 | 3.16 | 0.58 | 9.8 | 6 |
| 4 | 7.4 | 0.70 | 0.00 | 1.9 | 0.076 | 11.0 | 34.0 | 0.9978 | 3.51 | 0.56 | 9.4 | 5 |
데이터의 형태를 확인해보자.
red_wine.shape
(1599, 12)
각 데이터프레임에 color 열을 만들어 레드 와인은 1, 화이트 와인은 0으로 와인 색상을 추가한 다음 둘을 합쳐 하나의 데이터프레임으로 만든다.
red_wine['color'] = 1
white_wine['color'] = 0
wine = pd.concat([red_wine, white_wine])
연속적인 변수를 예측하는 경우를 회귀(regression)라고 한다.
기계학습에서 예측은 독립변수(x)를 이용해 종속변수(y)를 예측한다. 데이터에서 독립변수와 종속변수를 나눠준다. 여기서는 'quality'를 종속변수로 하고, 나머지를 독립변수로 한다.
x = wine[red_wine.columns.difference(['quality'])]
y = wine['quality']
기계학습을 할 때는 학습을 시킬 훈련용 데이터와 학습된 성능을 검증할 테스트용 데이터를 나눈다. 아래 코드는 wine 데이터 중 무작위로 20%를 테스트용으로 배정한다. random_state를 아래와 같이 고정해주면 코드를 실행할 때마다 같은 방식으로 데이터를 나눈다.
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=.2, random_state=1234)
먼저 최근접 이웃(k Nearest Neighbors: kNN) 방법을 사용해보자. kNN은 가장 유사한 k개의 사례들을 평균 내는 방식으로 예측을 한다.
from sklearn.neighbors import KNeighborsRegressor
knn = KNeighborsRegressor(n_neighbors=5) # 5개의 유사사례를 참고하는 모형
훈련용 데이터를 이용해 학습을 시킨다.
knn.fit(x_train, y_train)
KNeighborsRegressor(algorithm='auto', leaf_size=30, metric='minkowski',
metric_params=None, n_jobs=1, n_neighbors=5, p=2,
weights='uniform')
이제 테스트 데이터를 이용해 예측을 한다.
y_knn = knn.predict(x_test)
먼저 시각화를 통해 예측과 실제를 비교해보자.
%matplotlib inline
import seaborn as sns
sns.set_style('darkgrid')
sns.regplot(x=y_test, y=y_knn, fit_reg=False, x_jitter=0.4, scatter_kws={'alpha': 0.1})
<matplotlib.axes._subplots.AxesSubplot at 0x1998657e278>
<matplotlib.figure.Figure at 0x199865866a0>
다음으로는 수치화된 비교를 해보자.
from sklearn.metrics import mean_squared_error, r2_score
MSE는 오차의 제곱의 평균이다.
mean_squared_error(y_test, y_knn)
0.64267692307692315
MSE 자체만으로는 오차가 얼마나 큰지 알기 어렵기 때문에 실제값의 분산과 비교를 한 R제곱을 많이 사용한다. R제곱은 1 - MSE/분산으로 계산하며 예측이 분산의 몇 %를 설명하는지로 해석한다.
r2_score(y_test, y_knn)
0.1617311439983018
전처리를 잘 해주면 기계학습의 성능이 향상될 수 있다. 특히 kNN의 경우에는 비슷한 사례를 찾기 때문에 각 변수를 표준화시켜주면 성능이 더 향상된다. 표준화란 변수 값에서 평균을 빼고 표준편차를 나눠서, 모든 변수의 평균이 0, 표준편차가 1이 되도록 조정해주는 것이다.
from sklearn.preprocessing import StandardScaler
stand = StandardScaler()
x_train_std = stand.fit_transform(x_train) # 훈련용 데이터를 표준화한다
x_test_std = stand.transform(x_test) # 훈련용과 같은 방식으로 변환한다
다시 모형을 만들어 학습을 시키고 성능을 검증해본다.
knn2 = KNeighborsRegressor(n_neighbors=5)
knn2.fit(x_train_std, y_train)
KNeighborsRegressor(algorithm='auto', leaf_size=30, metric='minkowski',
metric_params=None, n_jobs=1, n_neighbors=5, p=2,
weights='uniform')
y_knn2 = knn2.predict(x_test_std)
눈으로 보기엔 큰 차이가 없어보인다.
sns.regplot(x=y_test, y=y_knn2, fit_reg=False, x_jitter=0.4, scatter_kws={'alpha': 0.1})
<matplotlib.axes._subplots.AxesSubplot at 0x199864d92e8>
<matplotlib.figure.Figure at 0x19986517358>
지표로 보면 성능이 더 향상된 것을 확인할 수 있다.
r2_score(y_test, y_knn2)
0.37813108997240807
선형 모형은 각 변수에 일정한 가중치를 곱하여 총점을 구하는 방식으로 예측하는 방법이다.
from sklearn.linear_model import LinearRegression
lm = LinearRegression()
lm.fit(x_train, y_train)
LinearRegression(copy_X=True, fit_intercept=True, n_jobs=1, normalize=False)
y_lm = lm.predict(x_test)
sns.regplot(x=y_test, y=y_lm, fit_reg=False, x_jitter=0.4, scatter_kws={'alpha': 0.1})
<matplotlib.axes._subplots.AxesSubplot at 0x199865b23c8>
<matplotlib.figure.Figure at 0x199865ed1d0>
r2_score(y_test, y_lm)
0.2943666891047606
선형 모형에도 표준화된 데이터를 적용하면 성능이 향상되는지 확인을 해보자.
lm2 = LinearRegression()
lm2.fit(x_train_std, y_train)
LinearRegression(copy_X=True, fit_intercept=True, n_jobs=1, normalize=False)
y_lm2 = lm2.predict(x_test_std)
표준화 하기 전과 성능에 큰 차이가 없다. 모형에 따라 수학적 특성이 다르기 때문에 전처리의 효과도 다르다.
r2_score(y_test, y_lm2)
0.29436668910476005
'이것 아니면 저것' 식으로 예측하는 것은 분류라고 한다. 이번에는 레드/화이트 와인을 예측해보자.
xc = wine[red_wine.columns.difference(['color'])]
yc = wine['color']
역시 데이터 분할을 해준다.
xc_train, xc_test, yc_train, yc_test = train_test_split(xc, yc, test_size=.2, random_state=1234)
분류에도 최근접 이웃이 있다. 회귀의 경우와 다르게 평균 대신 '다수결'로 예측을 한다. 유사 사례 중에 레드 와인이 많으면 레드로, 화이트 와인이이 많으면 화이트로 예측한다.
from sklearn.neighbors import KNeighborsClassifier
knc = KNeighborsClassifier(n_neighbors=5)
knc.fit(xc_train, yc_train)
KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski',
metric_params=None, n_jobs=1, n_neighbors=5, p=2,
weights='uniform')
y_knc = knc.predict(xc_test)
분류에서는 오차가 없기 때문에 MSE나 R제곱 대신 각 사례를 얼마나 잘 분류했는지를 확인한다.
from sklearn.metrics import confusion_matrix
먼저 혼돈행렬을 확인해본다. 혼돈행렬은 행이 실제 값을, 열이 예측값을 나타낸다. 즉, 실제로 0이고 예측도 0인 경우가 935건, 실제로 0이지만 1로 예측한 경우가 30건, 실제로 1이지만 0으로 예측한 경우가 43건, 실제로도 1이고 예측도 1인 경우가 292건이다.
confusion_matrix(yc_test, y_knc)
array([[935, 30],
[ 43, 292]])
혼돈 행렬을 이용해 여러 가지 지표를 만들 수 있는데 그 중 대표적인 것이 정확도(accuracy)이다. 정확도는 전체 경우에서 예측과 실제가 일치한 바율을 나타낸다.
from sklearn.metrics import accuracy_score
accuracy_score(yc_test, y_knc)
0.94384615384615389
로지스틱 회귀분석은 선형 모형을 분류에 응용한 것이다. 가중치를 곱해서 총점을 구하는 부분까지는 똑같지만 그 총점에 로지스틱 함수를 적용해 0~1 범위의 값으로 예측한다. 만약 예측값이 기준(예: 0.5)보다 크면 1로 작으면 0으로 예측하는 방식이다.
from sklearn.linear_model import LogisticRegression
lr = LogisticRegression()
lr.fit(xc_train, yc_train)
LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True,
intercept_scaling=1, max_iter=100, multi_class='ovr', n_jobs=1,
penalty='l2', random_state=None, solver='liblinear', tol=0.0001,
verbose=0, warm_start=False)
y_lr = lr.predict(xc_test)
혼돈 행렬을 만들어본다.
confusion_matrix(yc_test, y_lr)
array([[955, 10],
[ 13, 322]])
정확도를 계산해본다.
accuracy_score(yc_test, y_lr)
0.98230769230769233